UNPKG

nerve-sdk-js

Version:
163 lines (132 loc) 4.79 kB
var nerve = require("../index"); var MAX_SAFE_INTEGER = 9007199254740991; function checkUInt53(n) { if (n < 0 || n > MAX_SAFE_INTEGER || n % 1 !== 0) throw new RangeError('value out of range'); } function encodingLength(number) { checkUInt53(number); return number < 0xfd ? 1 : number <= 0xffff ? 3 : number <= 0xffffffff ? 5 : 9; } /** * calc split number of Transaction Change Spliting by splitGranularity * change = fromTotal - transfer - fee * change = splitNum * splitGranularity * fee = txSize * feeRate * txSize = f(splitNum) * f(splitNum) = f(1) + 43 * (splitNum - 1) ==> Derived from btc.calcTxSizeWithdrawal(inputNum, splitNum) * In summary: * splitNum = (fromTotal - transfer - btc.calcTxSizeWithdrawal(inputNum, 1) * feeRate + 43 * feeRate) / (43 * feeRate + splitGranularity) */ function calcSplitNumP2SH(fromTotal, transfer, feeRate, splitGranularity, inputNum) { // numerator and denominator var numerator = fromTotal - transfer - calcTxSizeWithdrawal(inputNum, 1) * feeRate + 43 * feeRate; var denominator = 43 * feeRate + splitGranularity; var splitNum = int(numerator / denominator); if (splitNum == 0 && numerator % denominator > 0) { splitNum = 1; } return splitNum; } function calcFeeMultiSign(inputNum, outputNum, opReturnBytesLen, m, n) { var op_mLen = 1; var op_nLen = 1; var pubKeyLen = 33; var pubKeyLenLen = 1; var op_checkmultisigLen = 1; var redeemScriptLength = op_mLen + n * (pubKeyLenLen + pubKeyLen) + op_nLen + op_checkmultisigLen; //105 n=3 var redeemScriptVarInt = encodingLength(redeemScriptLength); //1 n=3 var op_pushDataLen = 1; var sigHashLen = 1; var signLen = 64; var signLenLen = 1; var zeroByteLen = 1; var mSignLen = m * (signLenLen + signLen + sigHashLen); //132 m=2 var scriptLength = zeroByteLen + mSignLen + op_pushDataLen + redeemScriptVarInt + redeemScriptLength; //236 m=2 var scriptVarInt = encodingLength(scriptLength); var preTxIdLen = 32; var preIndexLen = 4; var sequenceLen = 4; var inputLength = preTxIdLen + preIndexLen + sequenceLen + scriptVarInt + scriptLength; //240 n=3,m=2 var opReturnLen = 0; if (opReturnBytesLen != 0) opReturnLen = calcOpReturnLen(opReturnBytesLen); var outputValueLen = 8; var unlockScriptLen = 25; //If sending to multiSignAddr, it will be 23. var unlockScriptLenLen = 1; var outPutLen = outputValueLen + unlockScriptLenLen + unlockScriptLen; var inputCountLen = 1; var outputCountLen = 1; var txVerLen = 4; var nLockTimeLen = 4; var txFixedLen = inputCountLen + outputCountLen + txVerLen + nLockTimeLen; var length; length = txFixedLen + inputLength * inputNum + outPutLen * (outputNum + 1) + opReturnLen; return length; } function calcOpReturnLen(opReturnBytesLen) { var dataLen; if (opReturnBytesLen < 76) { dataLen = opReturnBytesLen + 1; } else if (opReturnBytesLen < 256) { dataLen = opReturnBytesLen + 2; } else dataLen = opReturnBytesLen + 3; var scriptLen; scriptLen = dataLen + 1 + encodingLength(dataLen + 1); var amountLen = 8; return scriptLen + amountLen; } function calcTxSizeWithdrawal(utxoSize) { var outputNum = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var m, n; if (nerve.chainId() == 9) { m = 10, n = 15; } else { m = 2, n = 3; } return calcFeeMultiSign(utxoSize, outputNum, 64, m, n); } var fch = { getFeeRate() { return 1; }, calcFeeWithdrawal(utxos, amount, feeRate) { var splitGranularity = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; if (Array.isArray(utxos)) { utxos.sort((a, b) => { if (a.value !== b.value) { return Number(a.value) < Number(b.value) ? -1 : 1; } else if (a.txid !== b.txid) { return a.txid < b.txid ? -1 : 1; } else if (a.vout !== b.vout) { return Number(a.vout) < Number(b.vout) ? -1 : 1; } else { return 0; } }); } var _fee = 0, total = 0, totalSpend = 0; var resultList = []; amount = Number(amount); for (var i = 0; i < utxos.length; i++) { var utxo = utxos[i]; total = total + utxo.amount; resultList.push(utxo); if (splitGranularity > 0) { var splitNum = calcSplitNumP2SH(total, amount, feeRate, splitGranularity, resultList.length); _fee = calcTxSizeWithdrawal(resultList.length, splitNum) * feeRate; } else { _fee = calcTxSizeWithdrawal(resultList.length) * feeRate; } totalSpend = amount + _fee; if (total >= totalSpend) { break; } } if (total < totalSpend) { throw "not enough fee"; } return _fee; } }; module.exports = fch;