forked from Atomicwallet/coinselect
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.js
195 lines (173 loc) · 6.29 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
const BN = require('bn.js')
const ext = require('./bn-extensions')
const SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_SYSCOIN = 128
const SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION = 129
const SYSCOIN_TX_VERSION_ASSET_ACTIVATE = 130
const SYSCOIN_TX_VERSION_ASSET_UPDATE = 131
const SYSCOIN_TX_VERSION_ASSET_SEND = 132
const SYSCOIN_TX_VERSION_ALLOCATION_MINT = 133
const SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_ETHEREUM = 134
const SYSCOIN_TX_VERSION_ALLOCATION_SEND = 135
function isNonAssetFunded (txVersion) {
return txVersion === SYSCOIN_TX_VERSION_ASSET_SEND || txVersion === SYSCOIN_TX_VERSION_ASSET_ACTIVATE || txVersion === SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION || txVersion === SYSCOIN_TX_VERSION_ALLOCATION_MINT
}
function isAsset (txVersion) {
return txVersion === SYSCOIN_TX_VERSION_ASSET_ACTIVATE || txVersion === SYSCOIN_TX_VERSION_ASSET_UPDATE || txVersion === SYSCOIN_TX_VERSION_ASSET_SEND
}
function isAllocationBurn (txVersion) {
return txVersion === SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_SYSCOIN || txVersion === SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_ETHEREUM
}
function isAssetAllocationTx (txVersion) {
return txVersion === SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_ETHEREUM || txVersion === SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_SYSCOIN || txVersion === SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION || txVersion === SYSCOIN_TX_VERSION_ALLOCATION_SEND
}
// baseline estimates, used to improve performance
const TX_BASE_SIZE = new BN(11)
const TX_INPUT_SIZE = {
LEGACY: new BN(147),
P2SH: new BN(91),
BECH32: new BN(68)
}
const TX_OUTPUT_SIZE = {
LEGACY: new BN(34),
P2SH: new BN(32),
BECH32: new BN(31)
}
function inputBytes (input) {
return TX_INPUT_SIZE[input.type] || TX_INPUT_SIZE.LEGACY
}
function outputBytes (output) {
if (output.script) {
return new BN(output.script.length + 5 + 8) // 5 for OP_PUSHDATA2 max OP_RETURN prefix, 8 for amount
}
return TX_OUTPUT_SIZE[output.type] || TX_OUTPUT_SIZE.LEGACY
}
function dustThreshold (output, feeRate) {
/* ... classify the output for input estimate */
return ext.mul(inputBytes(output), feeRate)
}
function transactionBytes (inputs, outputs) {
return TX_BASE_SIZE
.add(inputs.reduce(function (a, x) {
return ext.add(a, inputBytes(x))
}, ext.BN_ZERO))
.add(outputs.reduce(function (a, x) {
return ext.add(a, outputBytes(x))
}, ext.BN_ZERO))
}
function uintOrNull (v) {
if (!BN.isBN(v)) return null
if (v.isNeg()) return null
return v
}
function sumForgiving (range) {
return range.reduce(function (a, x) {
const valueOrZero = BN.isBN(x.value) ? x.value : ext.BN_ZERO
return ext.add(a, valueOrZero)
},
ext.BN_ZERO)
}
function sumOrNaN (range, txVersion) {
return range.reduce(function (a, x) {
const value = x.value
return ext.add(a, uintOrNull(value))
}, ext.BN_ZERO)
}
function hasZeroVal (range) {
for (let i = 0; i < range.length; i++) {
if (range[i].value.isZero()) { return true }
}
return false
}
function finalize (inputs, outputs, feeRate, feeBytes, txVersion) {
const bytesAccum = transactionBytes(inputs, outputs)
const feeAfterExtraOutput = ext.mul(feeRate, ext.add(bytesAccum, feeBytes))
const remainderAfterExtraOutput = ext.sub(sumOrNaN(inputs), ext.add(sumOrNaN(outputs, txVersion), feeAfterExtraOutput))
// is it worth a change output?
if (ext.gt(remainderAfterExtraOutput, dustThreshold({}, feeRate))) {
outputs = outputs.concat({ changeIndex: outputs.length, value: remainderAfterExtraOutput })
}
const fee = ext.sub(sumOrNaN(inputs), sumOrNaN(outputs, txVersion))
if (!fee) return { fee: ext.mul(feeRate, bytesAccum) }
return {
inputs: inputs,
outputs: outputs,
fee: fee
}
}
function finalizeAssets (inputs, outputs, assetAllocations) {
if (!inputs || !outputs || !assetAllocations) {
return {
inputs: null,
outputs: null,
assetAllocations: null
}
}
return {
inputs: inputs,
outputs: outputs,
assetAllocations: assetAllocations
}
}
function getAuxFee (auxfeedetails, nAmount) {
let nAccumulatedFee = 0
let nBoundAmount = 0
let nNextBoundAmount = 0
let nRate = 0
for (let i = 0; i < auxfeedetails.auxfees.length; i++) {
const fee = auxfeedetails.auxfees[i]
const feeNext = auxfeedetails.auxfees[i < auxfeedetails.auxfees.length - 1 ? i + 1 : i]
nBoundAmount = fee.bound || 0
nNextBoundAmount = feeNext.bound
// max uint16 (65535 = 0.65535 = 65.5535%)
if (fee.percent) {
nRate = fee.percent / 100000.0
} else {
nRate = 0
}
// case where amount is in between the bounds
if (nAmount >= nBoundAmount && nAmount < nNextBoundAmount) {
break
}
nBoundAmount = nNextBoundAmount - nBoundAmount
// must be last bound
if (nBoundAmount <= 0) {
return new BN((nAmount - nNextBoundAmount) * nRate + nAccumulatedFee)
}
nAccumulatedFee += (nBoundAmount * nRate)
}
return new BN((nAmount - nBoundAmount) * nRate + nAccumulatedFee)
}
function createAssetID (NFTID, assetGuid) {
const BN_ASSET = new BN(NFTID || 0).shln(32).or(new BN(assetGuid))
return BN_ASSET.toString(10)
}
function getBaseAssetID (assetGuid) {
return new BN(assetGuid).and(new BN(0xFFFFFFFF)).toString(10)
}
module.exports = {
dustThreshold: dustThreshold,
finalize: finalize,
finalizeAssets: finalizeAssets,
inputBytes: inputBytes,
outputBytes: outputBytes,
sumOrNaN: sumOrNaN,
sumForgiving: sumForgiving,
transactionBytes: transactionBytes,
uintOrNull: uintOrNull,
getAuxFee: getAuxFee,
SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_SYSCOIN: SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_SYSCOIN,
SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION: SYSCOIN_TX_VERSION_SYSCOIN_BURN_TO_ALLOCATION,
SYSCOIN_TX_VERSION_ASSET_ACTIVATE: SYSCOIN_TX_VERSION_ASSET_ACTIVATE,
SYSCOIN_TX_VERSION_ASSET_UPDATE: SYSCOIN_TX_VERSION_ASSET_UPDATE,
SYSCOIN_TX_VERSION_ASSET_SEND: SYSCOIN_TX_VERSION_ASSET_SEND,
SYSCOIN_TX_VERSION_ALLOCATION_MINT: SYSCOIN_TX_VERSION_ALLOCATION_MINT,
SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_ETHEREUM: SYSCOIN_TX_VERSION_ALLOCATION_BURN_TO_ETHEREUM,
SYSCOIN_TX_VERSION_ALLOCATION_SEND: SYSCOIN_TX_VERSION_ALLOCATION_SEND,
isNonAssetFunded: isNonAssetFunded,
isAsset: isAsset,
isAllocationBurn: isAllocationBurn,
hasZeroVal: hasZeroVal,
isAssetAllocationTx: isAssetAllocationTx,
createAssetID: createAssetID,
getBaseAssetID: getBaseAssetID
}