fix(generateSendOpReturnWeb): Restoring old SLP Send for web

This commit is contained in:
Chris Troutner
2020-07-08 10:32:07 -07:00
parent f08838cd6b
commit e881d24d08
2 changed files with 205 additions and 0 deletions
+86
View File
@@ -2,6 +2,7 @@
//const bchjs = new BCHJS()
const Address = require("./address")
const Script = require("../script")
const BigNumber = require("bignumber.js")
const slpMdm = require("slp-mdm")
@@ -15,6 +16,7 @@ class TokenType1 {
this.restURL = config.restURL
addy = new Address(config)
this.Script = new Script()
// Instantiate the transaction builder.
TransactionBuilder.setAddress(addy)
@@ -123,6 +125,90 @@ class TokenType1 {
}
}
/**
* @api SLP.TokenType1.generateSendOpReturnWeb() generateSendOpReturnWeb() - Web-friendly OP_RETURN code for SLP Send tx
* @apiName generateSendOpReturnWeb
* @apiGroup SLP
* @apiDescription Generate the OP_RETURN value needed to create an SLP Send transaction.
* It's assumed all elements in the tokenUtxos array belong to the same token.
* Returns an object with two properties:
* - script: an array of Bufers that is ready to fed into bchjs.Script.encode2() to be turned into a transaction output.
* - outputs: an integer with a value of 1 or 2. If 2, indicates there needs to be an extra output to send token change.
*/
generateSendOpReturnWeb(tokenUtxos, sendQty) {
try {
const tokenId = tokenUtxos[0].tokenId
const decimals = tokenUtxos[0].decimals
// Calculate the total amount of tokens owned by the wallet.
let totalTokens = 0
for (let i = 0; i < tokenUtxos.length; i++)
totalTokens += tokenUtxos[i].tokenQty
const change = totalTokens - sendQty
// console.log(`change: ${change}`)
let script
let outputs = 1
// The normal case, when there is token change to return to sender.
if (change > 0) {
outputs = 2
let baseQty = new BigNumber(sendQty).times(10 ** decimals)
baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty)
let baseQtyHex = baseQty.toString(16)
baseQtyHex = baseQtyHex.padStart(16, "0")
let baseChange = new BigNumber(change).times(10 ** decimals)
baseChange = baseChange.absoluteValue()
baseChange = Math.floor(baseChange)
// console.log(`baseChange: ${baseChange.toString()}`)
let baseChangeHex = baseChange.toString(16)
baseChangeHex = baseChangeHex.padStart(16, "0")
// console.log(`baseChangeHex padded: ${baseChangeHex}`)
script = [
this.Script.opcodes.OP_RETURN,
Buffer.from("534c5000", "hex"),
//BITBOX.Script.opcodes.OP_1,
Buffer.from("01", "hex"),
Buffer.from(`SEND`),
Buffer.from(tokenId, "hex"),
Buffer.from(baseQtyHex, "hex"),
Buffer.from(baseChangeHex, "hex")
]
} else {
// Corner case, when there is no token change to send back.
let baseQty = new BigNumber(sendQty).times(10 ** decimals)
baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty)
let baseQtyHex = baseQty.toString(16)
baseQtyHex = baseQtyHex.padStart(16, "0")
// console.log(`baseQty: ${baseQty.toString()}`)
script = [
this.Script.opcodes.OP_RETURN,
Buffer.from("534c5000", "hex"),
//BITBOX.Script.opcodes.OP_1,
Buffer.from("01", "hex"),
Buffer.from(`SEND`),
Buffer.from(tokenId, "hex"),
Buffer.from(baseQtyHex, "hex")
]
}
return { script, outputs }
} catch (err) {
console.log(`Error in generateSendOpReturnWeb()`)
throw err
}
}
/**
* @api SLP.TokenType1.generateBurnOpReturn() generateBurnOpReturn()
* @apiName generateBurnOpReturn
+119
View File
@@ -70,6 +70,125 @@ describe("#SLP TokenType1", () => {
})
})
describe("#generateSendOpReturnWeb", () => {
it("should generate send OP_RETURN code", async () => {
// Mock UTXO.
const tokenUtxos = [
{
txid:
"a8eb788b8ddda6faea00e6e2756624b8feb97655363d0400dd66839ea619d36e",
vout: 2,
value: "546",
confirmations: 0,
satoshis: 546,
utxoType: "token",
transactionType: "send",
tokenId:
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7",
tokenTicker: "TOK-CH",
tokenName: "TokyoCash",
tokenDocumentUrl: "",
tokenDocumentHash: "",
decimals: 8,
tokenQty: 7
}
]
const result = await bchjs.SLP.TokenType1.generateSendOpReturnWeb(
tokenUtxos,
1
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["script", "outputs"])
assert.isNumber(result.outputs)
})
it("should handle problematic configuration", async () => {
// Mock UTXO.
const tokenUtxos = [
{
txid:
"0bd6b874246202b4cbc2f501419a5ce6f9b01e8ba9521298afd15c7e8eac5951",
vout: 2,
value: "546",
confirmations: 0,
satoshis: 546,
utxoType: "token",
transactionType: "send",
tokenId:
"155784a206873c98acc09e8dabcccf6abf13c4c14d8662190534138a16bb93ce",
tokenTicker: "PSF",
tokenName: "PSF Testnet Token",
tokenDocumentUrl: "",
tokenDocumentHash: "",
decimals: 8,
tokenQty: 18004.71169917
}
]
const result = await bchjs.SLP.TokenType1.generateSendOpReturnWeb(
tokenUtxos,
5000
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result.script: `, result.script)
// console.log(`result.script: `, result.script.toString("hex"))
// This transaction failed due to a floating point error. This is expressed
// by the script[6] being length 2 (incorrect) instead of 8 (correct).
assert.equal(result.script[5].length, 8)
assert.equal(result.script[6].length, 8)
})
it("should generate good OP_RETURN for problematic TX", () => {
const utxo1 = {
txid:
"35287aa2aa7d3f1954fbbcb8a748d8773359b4f2771a851959a4a6116bcb552c",
vout: 1,
amount: 0.00000546,
satoshis: 546,
legacyAddress: "1MuLZ9LzLSTKioBHoPzeSfFANTEN2CZhuS",
cashAddress: "bitcoincash:qrj5sala6z53v559q54mekexru2xe34yrquhmpy5kx",
tokenId:
"c4b0d62156b3fa5c8f3436079b5394f7edc1bef5dc1cd2f9d0c4d46f82cca479",
decimals: 2,
tokenQty: 1371.6
}
const utxo2 = {
txid:
"cb652ce62babc81936f0f1e1d5b80102cb44de99dece83f4bbd34cfaeef744b0",
vout: 2,
amount: 0.00000546,
satoshis: 546,
legacyAddress: "1LzjkvDVB16k6kcSz8hZodW9KPP2zxwmGh",
cashAddress: "bitcoincash:qrd4tj48lpf0wv36qt5mkd6c3n4h5xcfgqqw77s63d",
tokenId:
"c4b0d62156b3fa5c8f3436079b5394f7edc1bef5dc1cd2f9d0c4d46f82cca479",
decimals: 2,
tokenQty: 3250.75
}
const utxos = [utxo1, utxo2]
const { script } = bchjs.SLP.TokenType1.generateSendOpReturnWeb(
utxos,
1400
)
// console.log(`script: `, script)
// console.log(`outputs: `, outputs)
// console.log(`script[6]: ${script[6].toString("hex")}`)
// console.log(`script[6].length: ${script[6].length}`)
// This transaction failed due to a floating point error. This is expressed
// by the script[6] being length 2 (incorrect) instead of 8 (correct).
assert.equal(script[6].length, 8)
})
})
describe("#generateBurnOpReturn", () => {
it("should generate burn OP_RETURN code", () => {
// Mock UTXO.