nerve-sdk-js
Version:
nerve nerve-js nerve-sdk nerve-js-sdk
163 lines (132 loc) • 4.79 kB
JavaScript
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;