UNPKG

slpjs

Version:

Simple Ledger Protocol (SLP) JavaScript Library

483 lines 25.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spread = (this && this.__spread) || function () { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TransactionHelpers = void 0; var __1 = require(".."); var bignumber_js_1 = __importDefault(require("bignumber.js")); var Bitcore = __importStar(require("bitcore-lib-cash")); var primatives_1 = require("./primatives"); var TransactionHelpers = /** @class */ (function () { function TransactionHelpers(slp) { this.slp = slp; } // Create raw transaction hex to: Send SLP tokens to one or more token receivers, include optional BCH only outputs TransactionHelpers.prototype.simpleTokenSend = function (_a) { var tokenId = _a.tokenId, sendAmounts = _a.sendAmounts, inputUtxos = _a.inputUtxos, tokenReceiverAddresses = _a.tokenReceiverAddresses, changeReceiverAddress = _a.changeReceiverAddress, _b = _a.requiredNonTokenOutputs, requiredNonTokenOutputs = _b === void 0 ? [] : _b, _c = _a.extraFee, extraFee = _c === void 0 ? 0 : _c; // normalize token receivers and amounts to array types if (typeof tokenReceiverAddresses === "string") { tokenReceiverAddresses = [tokenReceiverAddresses]; } try { var amount = sendAmounts; amount.forEach(function (a) { return a.isGreaterThan(new bignumber_js_1.default(0)); }); } catch (_) { sendAmounts = [sendAmounts]; } if (sendAmounts.length !== tokenReceiverAddresses.length) { throw Error("Must have send amount item for each token receiver specified."); } // 1) Set the token send amounts, we'll send 100 tokens to a // new receiver and send token change back to the sender var totalTokenInputAmount = inputUtxos .filter(function (txo) { return __1.Slp.preSendSlpJudgementCheck(txo, tokenId); }) .reduce(function (tot, txo) { return tot.plus(txo.slpUtxoJudgementAmount); }, new bignumber_js_1.default(0)); // 2) Compute the token Change amount. var tokenChangeAmount = totalTokenInputAmount.minus(sendAmounts.reduce(function (t, v) { return t = t.plus(v); }, new bignumber_js_1.default(0))); // Get token_type var token_type = inputUtxos.filter(function (i) { return i.slpUtxoJudgement === __1.SlpUtxoJudgement.SLP_TOKEN && i.slpTransactionDetails.tokenIdHex === tokenId; })[0].slpTransactionDetails.versionType; var txHex; if (tokenChangeAmount.isGreaterThan(new bignumber_js_1.default(0))) { // 3) Create the Send OP_RETURN message var sendOpReturn = __1.Slp.buildSendOpReturn({ tokenIdHex: tokenId, outputQtyArray: __spread(sendAmounts, [tokenChangeAmount]), }, token_type); // 4) Create the raw Send transaction hex txHex = this.slp.buildRawSendTx({ slpSendOpReturn: sendOpReturn, input_token_utxos: __1.Utils.mapToUtxoArray(inputUtxos), tokenReceiverAddressArray: __spread(tokenReceiverAddresses, [changeReceiverAddress]), bchChangeReceiverAddress: changeReceiverAddress, requiredNonTokenOutputs: requiredNonTokenOutputs, extraFee: extraFee }); } else if (tokenChangeAmount.isEqualTo(new bignumber_js_1.default(0))) { // 3) Create the Send OP_RETURN message var sendOpReturn = __1.Slp.buildSendOpReturn({ tokenIdHex: tokenId, outputQtyArray: __spread(sendAmounts), }); // 4) Create the raw Send transaction hex txHex = this.slp.buildRawSendTx({ slpSendOpReturn: sendOpReturn, input_token_utxos: __1.Utils.mapToUtxoArray(inputUtxos), tokenReceiverAddressArray: __spread(tokenReceiverAddresses), bchChangeReceiverAddress: changeReceiverAddress, requiredNonTokenOutputs: requiredNonTokenOutputs, extraFee: extraFee }); } else { throw Error('Token inputs less than the token outputs'); } // Return raw hex for this transaction return txHex; }; // Create raw transaction hex to: Send BCH to one or more receivers, makes sure tokens are not burned TransactionHelpers.prototype.simpleBchSend = function (_a) { var sendAmounts = _a.sendAmounts, inputUtxos = _a.inputUtxos, bchReceiverAddresses = _a.bchReceiverAddresses, changeReceiverAddress = _a.changeReceiverAddress; // normalize token receivers and amounts to array types if (typeof bchReceiverAddresses === "string") { bchReceiverAddresses = [bchReceiverAddresses]; } if (typeof sendAmounts === "string") { sendAmounts = [sendAmounts]; } try { var amount = sendAmounts; amount.forEach(function (a) { return a.isGreaterThan(new bignumber_js_1.default(0)); }); } catch (_) { sendAmounts = [sendAmounts]; } if (sendAmounts.length !== bchReceiverAddresses.length) { throw Error("Must have send amount item for each token receiver specified."); } // 4) Create the raw Send transaction hex var txHex = this.slp.buildRawBchOnlyTx({ input_token_utxos: __1.Utils.mapToUtxoArray(inputUtxos), bchReceiverAddressArray: bchReceiverAddresses, bchReceiverSatoshiAmounts: sendAmounts, bchChangeReceiverAddress: changeReceiverAddress }); // Return raw hex for this transaction return txHex; }; // Create raw transaction hex to: Create a token Genesis issuance TransactionHelpers.prototype.simpleTokenGenesis = function (_a) { var tokenName = _a.tokenName, tokenTicker = _a.tokenTicker, tokenAmount = _a.tokenAmount, documentUri = _a.documentUri, documentHash = _a.documentHash, decimals = _a.decimals, tokenReceiverAddress = _a.tokenReceiverAddress, batonReceiverAddress = _a.batonReceiverAddress, bchChangeReceiverAddress = _a.bchChangeReceiverAddress, inputUtxos = _a.inputUtxos; var genesisOpReturn = __1.Slp.buildGenesisOpReturn({ ticker: tokenTicker, name: tokenName, documentUri: documentUri, hash: documentHash, decimals: decimals, batonVout: batonReceiverAddress ? 2 : null, initialQuantity: tokenAmount, }); // 4) Create/sign the raw transaction hex for Genesis var genesisTxHex = this.slp.buildRawGenesisTx({ slpGenesisOpReturn: genesisOpReturn, mintReceiverAddress: tokenReceiverAddress, batonReceiverAddress: batonReceiverAddress, bchChangeReceiverAddress: bchChangeReceiverAddress, input_utxos: __1.Utils.mapToUtxoArray(inputUtxos) }); // Return raw hex for this transaction return genesisTxHex; }; TransactionHelpers.prototype.simpleNFT1ParentGenesis = function (_a) { var tokenName = _a.tokenName, tokenTicker = _a.tokenTicker, tokenAmount = _a.tokenAmount, documentUri = _a.documentUri, documentHash = _a.documentHash, tokenReceiverAddress = _a.tokenReceiverAddress, batonReceiverAddress = _a.batonReceiverAddress, bchChangeReceiverAddress = _a.bchChangeReceiverAddress, inputUtxos = _a.inputUtxos, _b = _a.decimals, decimals = _b === void 0 ? 0 : _b; var genesisOpReturn = __1.Slp.buildGenesisOpReturn({ ticker: tokenTicker, name: tokenName, documentUri: documentUri, hash: documentHash, decimals: decimals, batonVout: batonReceiverAddress ? 2 : null, initialQuantity: tokenAmount, }, 0x81); // Create/sign the raw transaction hex for Genesis var genesisTxHex = this.slp.buildRawGenesisTx({ slpGenesisOpReturn: genesisOpReturn, mintReceiverAddress: tokenReceiverAddress, batonReceiverAddress: batonReceiverAddress, bchChangeReceiverAddress: bchChangeReceiverAddress, input_utxos: __1.Utils.mapToUtxoArray(inputUtxos) }); // Return raw hex for this transaction return genesisTxHex; }; TransactionHelpers.prototype.simpleNFT1ChildGenesis = function (_a) { var nft1GroupId = _a.nft1GroupId, tokenName = _a.tokenName, tokenTicker = _a.tokenTicker, documentUri = _a.documentUri, documentHash = _a.documentHash, tokenReceiverAddress = _a.tokenReceiverAddress, bchChangeReceiverAddress = _a.bchChangeReceiverAddress, inputUtxos = _a.inputUtxos, _b = _a.allowBurnAnyAmount, allowBurnAnyAmount = _b === void 0 ? false : _b; var genesisOpReturn = __1.Slp.buildGenesisOpReturn({ ticker: tokenTicker, name: tokenName, documentUri: documentUri, hash: documentHash, decimals: 0, batonVout: null, initialQuantity: new bignumber_js_1.default(1), }, 0x41); // make sure that the first input item is a a token from the parent nft1GroupId if (inputUtxos[0].slpUtxoJudgement !== __1.SlpUtxoJudgement.SLP_TOKEN) { throw Error("First input does not include a valid SLP NFT1 parent token."); } else if (inputUtxos[0].slpTransactionDetails.tokenIdHex !== nft1GroupId) { throw Error("First input does not include a valid parent token with the specified group id."); } else if (!allowBurnAnyAmount && !inputUtxos[0].slpUtxoJudgementAmount.isEqualTo(1)) { throw Error("NFT1 parent token burn amount is not 1. If you would like to allow burning quanity != 1 you can set the 'allowBurnAnyAmount' parameter."); } // Create/sign the raw transaction hex for Genesis var genesisTxHex = this.slp.buildRawGenesisTx({ slpGenesisOpReturn: genesisOpReturn, mintReceiverAddress: tokenReceiverAddress, batonReceiverAddress: null, bchChangeReceiverAddress: bchChangeReceiverAddress, input_utxos: __1.Utils.mapToUtxoArray(inputUtxos), allowed_token_burning: [nft1GroupId] }); // Return raw hex for this transaction return genesisTxHex; }; // Create raw transaction hex to: Mint new tokens or move the minting baton TransactionHelpers.prototype.simpleTokenMint = function (_a) { // // convert address to cashAddr from SLP format. // let fundingAddress_cashfmt = bchaddr.toCashAddress(fundingAddress); var tokenId = _a.tokenId, mintAmount = _a.mintAmount, inputUtxos = _a.inputUtxos, tokenReceiverAddress = _a.tokenReceiverAddress, tokenReceiverSatoshis = _a.tokenReceiverSatoshis, batonReceiverAddress = _a.batonReceiverAddress, changeReceiverAddress = _a.changeReceiverAddress, _b = _a.extraFee, extraFee = _b === void 0 ? 0 : _b, _c = _a.disableBchChangeOutput, disableBchChangeOutput = _c === void 0 ? false : _c, batonReceiverSatoshis = _a.batonReceiverSatoshis; var token_type = inputUtxos.filter(function (i) { return i.slpUtxoJudgement === __1.SlpUtxoJudgement.SLP_BATON; })[0].slpTransactionDetails.versionType; // 1) Create the Send OP_RETURN message var mintOpReturn = __1.Slp.buildMintOpReturn({ batonVout: 2, mintQuantity: mintAmount, tokenIdHex: tokenId, }, token_type); // 2) Create the raw Mint transaction hex var txHex = this.slp.buildRawMintTx({ input_baton_utxos: __1.Utils.mapToUtxoArray(inputUtxos), slpMintOpReturn: mintOpReturn, mintReceiverAddress: tokenReceiverAddress, mintReceiverSatoshis: tokenReceiverSatoshis, batonReceiverAddress: batonReceiverAddress, batonReceiverSatoshis: batonReceiverSatoshis, bchChangeReceiverAddress: changeReceiverAddress, extraFee: extraFee, disableBchChangeOutput: disableBchChangeOutput, }); // Return raw hex for this transaction return txHex; }; // Create raw transaction hex to: Burn a precise quantity of SLP tokens // with remaining tokens (change) sent to a single output address TransactionHelpers.prototype.simpleTokenBurn = function (_a) { var tokenId = _a.tokenId, burnAmount = _a.burnAmount, inputUtxos = _a.inputUtxos, changeReceiverAddress = _a.changeReceiverAddress; // Set the token send amounts var totalTokenInputAmount = inputUtxos .filter(function (txo) { return __1.Slp.preSendSlpJudgementCheck(txo, tokenId); }) .reduce(function (tot, txo) { return tot.plus(txo.slpUtxoJudgementAmount); }, new bignumber_js_1.default(0)); // Compute the token Change amount. var tokenChangeAmount = totalTokenInputAmount.minus(burnAmount); var txHex; if (tokenChangeAmount.isGreaterThan(new bignumber_js_1.default(0))) { // Create the Send OP_RETURN message var sendOpReturn = __1.Slp.buildSendOpReturn({ tokenIdHex: tokenId, outputQtyArray: [tokenChangeAmount], }); // Create the raw Send transaction hex txHex = this.slp.buildRawBurnTx(burnAmount, { slpBurnOpReturn: sendOpReturn, input_token_utxos: __1.Utils.mapToUtxoArray(inputUtxos), bchChangeReceiverAddress: changeReceiverAddress }); } else if (tokenChangeAmount.isLessThanOrEqualTo(new bignumber_js_1.default(0))) { // Create the raw Send transaction hex txHex = this.slp.buildRawBurnTx(burnAmount, { tokenIdHex: tokenId, input_token_utxos: __1.Utils.mapToUtxoArray(inputUtxos), bchChangeReceiverAddress: changeReceiverAddress }); } else { throw Error('Token inputs less than the token outputs'); } // Return raw hex for this transaction return txHex; }; TransactionHelpers.prototype.get_transaction_sig_filler = function (input_index, pubKeyBuf) { return { signatureBuf: Buffer.from('ff', 'hex'), pubKeyBuf: pubKeyBuf, index: input_index }; }; TransactionHelpers.prototype.get_transaction_sig_p2pkh = function (txHex, wif, input_index, input_satoshis, sigHashType) { // deserialize the unsigned transaction if (sigHashType === void 0) { sigHashType = 0x41; } var txn = new Bitcore.Transaction(txHex); // we need to get the key pair from wif // this will be used by bitcore-lib input sig generation // NOTE: Only works for compressed-WIF format var ecpair = this.slp.BITBOX.ECPair.fromWIF(wif); // we set the previous output for the input // again, this is for bitcore-lib input sig generation txn.inputs[input_index].output = new Bitcore.Transaction.Output({ satoshis: input_satoshis, script: Bitcore.Script.fromAddress(__1.Utils.toCashAddress(ecpair.getAddress())), }); // Update input to be non-abstract type so we can get the p2pkh sign method txn.inputs[input_index] = new Bitcore.Transaction.Input.PublicKeyHash(txn.inputs[input_index]); // produce a signature that is specific to this input // NOTE: currently only uses ecdsa var privateKey = new Bitcore.PrivateKey(wif); var sig = txn.inputs[input_index].getSignatures(txn, privateKey, input_index, sigHashType); // add have to add the sighash type manually.. :( // NOTE: signature is in DER format and is specific to ecdsa & sigHash 0x41 var sigBuf = Buffer.concat([sig[0].signature.toDER(), Buffer.alloc(1, sigHashType)]); // we can return a object conforming to InputSigData<P2pkhSig> interface return { index: input_index, pubKeyBuf: ecpair.getPublicKeyBuffer(), signatureBuf: sigBuf }; }; TransactionHelpers.prototype.get_transaction_sig_p2sh = function (txHex, wif, input_index, input_satoshis, redeemScript, scriptCode, sigHashType) { // deserialize the unsigned transaction if (sigHashType === void 0) { sigHashType = 0x41; } var txn = new Bitcore.Transaction(txHex); // we need to get the key pair from wif // this will be used by bitcore-lib input sig generation // NOTE: Only works for compressed-WIF format var ecpair = this.slp.BITBOX.ECPair.fromWIF(wif); // we set the previous output for the input // again, this is for bitcore-lib input sig generation txn.inputs[input_index].output = new Bitcore.Transaction.Output({ satoshis: input_satoshis, script: redeemScript }); // produce a signature that is specific to this input // NOTE: currently only uses ecdsa var privateKey = new Bitcore.PrivateKey(wif); var sig = Bitcore.Transaction.Sighash.sign(txn, privateKey, sigHashType, input_index, scriptCode, Bitcore.crypto.BN.fromNumber(input_satoshis)); // add have to add the sighash type manually.. :( // NOTE: signature is in DER format and is specific to ecdsa & sigHash 0x41 var sigBuf = Buffer.concat([sig.toDER(), Buffer.alloc(1, sigHashType)]); // we can return a object conforming to InputSigData<P2pkhSig> interface return { index: input_index, pubKeyBuf: ecpair.getPublicKeyBuffer(), signatureBuf: sigBuf }; }; TransactionHelpers.prototype.build_P2PKH_scriptSig = function (sigData) { return sigData; }; // build_P2PK_scriptSig(sigData: InputSigData): scriptSigP2PK { // return { // index: sigData.index, // signatureBuf: sigData.signatureBuf // } // } TransactionHelpers.prototype.build_P2SH_multisig_redeem_data = function (m, pubKeys) { // allow pubkeys to be passed in as strings pubKeys.forEach(function (k, i) { if (typeof k === "string") pubKeys[i] = Buffer.from(k, 'hex'); }); // use bitbox function to get multisig redeem script var redeemScript = this.slp.BITBOX.Script.encodeP2MSOutput(m, pubKeys); // compute this multisig address var addr = this.slp.BITBOX.Address.fromOutputScript(this.slp.BITBOX.Script.scriptHash.output.encode(this.slp.BITBOX.Crypto.hash160(redeemScript))); return { m: m, pubKeys: pubKeys, address: __1.Utils.toSlpAddress(addr), lockingScript: redeemScript }; }; TransactionHelpers.prototype.insert_input_values_for_EC_signers = function (txnHex, input_values) { var source = new primatives_1.Primatives.ArraySource(Array.from(Buffer.from(txnHex, 'hex').values())); var stream = new primatives_1.Primatives.ByteStream(source); var txn = primatives_1.Primatives.Transaction.parse(stream); input_values.forEach(function (v, i) { if (v && v > 0) { txn.inputs[i].satoshis = v; txn.inputs[i].incomplete = true; } }); return txn.toHex(); }; TransactionHelpers.prototype.build_P2SH_multisig_scriptSig = function (redeemData, input_index, sigs) { // check we have enough signatures if (sigs.length < redeemData.m) throw Error("Not enough signatures."); // check not too many signataures if (sigs.length > redeemData.pubKeys.length) throw Error("Too many pubKeys provided."); // check all provided signatures belong to the given possible pubkeys var pubKeysHex = redeemData.pubKeys.map(function (k) { return k.toString('hex'); }); var pubKeysGivenHex = sigs.map(function (d) { return d.pubKeyBuf.toString('hex'); }); pubKeysGivenHex.forEach(function (k) { if (!pubKeysHex.includes(k)) { throw Error("One of the public keys provided is a signer"); } }); // ordered sigs properly for OP_CHECKMULTISIG var orderedSigs = pubKeysHex.map(function (pub) { var sig = sigs.find(function (s) { return s.pubKeyBuf.toString('hex') === pub; }); return sig.signatureBuf; }); // build the unlocking script for multisig p2sh var unlockingScript = __spread([0x00], orderedSigs); //this.slp.BITBOX.Script.encodeP2MSInput(orderedSigs) return { index: input_index, lockingScriptBuf: redeemData.lockingScript, unlockingScriptBufArray: unlockingScript }; }; TransactionHelpers.prototype.addScriptSigs = function (unsignedTxnHex, scriptSigs) { // deserialize unsigned transaction so we can add sigs to it var _this = this; var txn = new Bitcore.Transaction(unsignedTxnHex); var bip62Encoded; scriptSigs.forEach(function (s) { // for p2pkh encode scriptSig if (s.pubKeyBuf) { var sigBuf = s.signatureBuf; var pubKeyBuf = s.pubKeyBuf; bip62Encoded = _this.slp.BITBOX.Script.encode([sigBuf, pubKeyBuf]); } // for p2sh encode scriptSig else if (s.lockingScriptBuf) { var unlockingBufArray = s.unlockingScriptBufArray; var lockingBuf = s.lockingScriptBuf; bip62Encoded = _this.slp.BITBOX.Script.encode(__spread(unlockingBufArray, [lockingBuf])); } // p2pk encode scriptSig else if (!s.pubKeyBuf && s.signatureBuf) { bip62Encoded = _this.slp.BITBOX.Script.encode([s.signatureBuf]); } // throw if input data did not result in encoded scriptSig if (!bip62Encoded) throw Error("Was not able to set input script for index=" + s.index); // actually set the input's scriptSig property var script = new Bitcore.Script(bip62Encoded); txn.inputs[s.index].setScript(script); // console.log("scriptSig for index", s.input_index, ":", bip62Encoded.toString('hex')) }); return txn.toString(); }; TransactionHelpers.prototype.setTxnLocktime = function (unsignedTxnHex, locktime) { var source = new primatives_1.Primatives.ArraySource(Array.from(Buffer.from(unsignedTxnHex, 'hex').values())); var stream = new primatives_1.Primatives.ByteStream(source); var txn = primatives_1.Primatives.Transaction.parse(stream); txn.lockTime = locktime; return txn.toHex(); }; TransactionHelpers.prototype.enableInputsCLTV = function (unsignedTxnHex) { var source = new primatives_1.Primatives.ArraySource(Array.from(Buffer.from(unsignedTxnHex, 'hex').values())); var stream = new primatives_1.Primatives.ByteStream(source); var txn = primatives_1.Primatives.Transaction.parse(stream); txn.inputs.forEach(function (input) { input.sequenceNo = 'fffffffe'; }); return txn.toHex(); }; return TransactionHelpers; }()); exports.TransactionHelpers = TransactionHelpers; //# sourceMappingURL=transactionhelpers.js.map