mainnet-js 2.3.16 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +1 -1
- package/dist/{mainnet-2.3.16.js → mainnet-2.4.1.js} +4 -4
- package/dist/module/history/electrumTransformer.d.ts +11 -3
- package/dist/module/history/electrumTransformer.d.ts.map +1 -1
- package/dist/module/history/electrumTransformer.js +199 -195
- package/dist/module/history/electrumTransformer.js.map +1 -1
- package/dist/module/history/interface.d.ts +19 -13
- package/dist/module/history/interface.d.ts.map +1 -1
- package/dist/module/index.d.ts +1 -0
- package/dist/module/index.d.ts.map +1 -1
- package/dist/module/index.js +1 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.d.ts +2 -2
- package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.js +6 -6
- package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
- package/dist/module/network/NetworkProvider.d.ts +1 -1
- package/dist/module/network/NetworkProvider.d.ts.map +1 -1
- package/dist/module/util/header.d.ts.map +1 -1
- package/dist/module/util/header.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts +25 -4
- package/dist/module/wallet/Wif.d.ts.map +1 -1
- package/dist/module/wallet/Wif.js +29 -7
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/history/electrumTransformer.test.ts +112 -55
- package/src/history/electrumTransformer.ts +279 -284
- package/src/history/interface.ts +19 -13
- package/src/index.ts +1 -0
- package/src/network/ElectrumNetworkProvider.ts +32 -9
- package/src/network/NetworkProvider.ts +5 -1
- package/src/util/header.test.ts +14 -8
- package/src/util/header.ts +1 -1
- package/src/wallet/Wif.ts +53 -19
|
@@ -1,319 +1,314 @@
|
|
|
1
1
|
import {
|
|
2
2
|
binToHex,
|
|
3
|
-
cashAddressToLockingBytecode,
|
|
4
3
|
decodeTransaction,
|
|
5
4
|
hexToBin,
|
|
6
|
-
Transaction,
|
|
7
5
|
lockingBytecodeToCashAddress,
|
|
8
|
-
|
|
6
|
+
CashAddressNetworkPrefix,
|
|
7
|
+
decodeCashAddress,
|
|
8
|
+
TransactionCommon,
|
|
9
9
|
} from "@bitauth/libauth";
|
|
10
10
|
import { UnitEnum } from "../enum.js";
|
|
11
11
|
import NetworkProvider from "../network/NetworkProvider.js";
|
|
12
|
-
import { derivePrefix } from "../util/derivePublicKeyHash.js";
|
|
13
12
|
import { convert } from "../util/convert.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
import { HeaderI, TokenI } from "../interface.js";
|
|
14
|
+
import { TransactionHistoryItem, InOutput } from "./interface.js";
|
|
15
|
+
|
|
16
|
+
type Transaction = TransactionCommon & {
|
|
17
|
+
size: number;
|
|
18
|
+
blockHeight: number;
|
|
19
|
+
timestamp?: number;
|
|
20
|
+
hash: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const getAddressHistory = async ({
|
|
24
|
+
address,
|
|
25
|
+
provider,
|
|
21
26
|
unit = "sat",
|
|
27
|
+
fromHeight = 0,
|
|
28
|
+
toHeight = -1,
|
|
22
29
|
start = 0,
|
|
23
|
-
count =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
)
|
|
46
|
-
|
|
30
|
+
count = -1,
|
|
31
|
+
}: {
|
|
32
|
+
address: string;
|
|
33
|
+
provider: NetworkProvider;
|
|
34
|
+
unit?: UnitEnum;
|
|
35
|
+
fromHeight?: number;
|
|
36
|
+
toHeight?: number;
|
|
37
|
+
start?: number;
|
|
38
|
+
count?: number;
|
|
39
|
+
}): Promise<TransactionHistoryItem[]> => {
|
|
40
|
+
if (count === -1) {
|
|
41
|
+
count = 1e10;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const history = (await provider.getHistory(address, fromHeight, toHeight))
|
|
45
|
+
.sort((a, b) =>
|
|
46
|
+
a.height <= 0 || b.height <= 0 ? a.height - b.height : b.height - a.height
|
|
47
|
+
)
|
|
48
|
+
.slice(start, start + count);
|
|
49
|
+
|
|
50
|
+
// fill transaction timestamps by requesting headers from network and parsing them
|
|
51
|
+
const heights = history
|
|
52
|
+
.map((tx) => tx.height)
|
|
53
|
+
.filter((height) => height > 0)
|
|
54
|
+
.filter((value, index, array) => array.indexOf(value) === index);
|
|
55
|
+
const timestampMap = (
|
|
56
|
+
await Promise.all(
|
|
57
|
+
heights.map(async (height) => [
|
|
58
|
+
height,
|
|
59
|
+
((await provider.getHeader(height, true)) as HeaderI).timestamp,
|
|
60
|
+
])
|
|
61
|
+
)
|
|
62
|
+
).reduce((acc, [height, timestamp]) => ({ ...acc, [height]: timestamp }), {});
|
|
63
|
+
|
|
64
|
+
// first load all transactions
|
|
65
|
+
const historicTransactions = await Promise.all(
|
|
66
|
+
history.map(async (tx) => {
|
|
67
|
+
const txHex = (await provider.getRawTransaction(tx.tx_hash)) as string;
|
|
68
|
+
|
|
69
|
+
const transactionCommon = decodeTransaction(hexToBin(txHex));
|
|
70
|
+
if (typeof transactionCommon === "string") {
|
|
71
|
+
throw transactionCommon;
|
|
72
|
+
}
|
|
47
73
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Get the factor to apply the requested unit of measure
|
|
58
|
-
let factor =
|
|
59
|
-
(await convert(bchParam.subUnits, "sat", unit)) / bchParam.subUnits;
|
|
60
|
-
|
|
61
|
-
// Apply the unit factor and
|
|
62
|
-
let txns = applyBalance(preprocessedTxns, currentBalance, unit, factor);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
transactions: txns,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function getDetailedHistory(
|
|
70
|
-
cashaddr: string,
|
|
71
|
-
hash: string,
|
|
72
|
-
height: number,
|
|
73
|
-
provider: NetworkProvider,
|
|
74
|
-
collapseChange: boolean
|
|
75
|
-
): Promise<TransactionHistoryItemI[]> {
|
|
76
|
-
let transactionHex = await provider.getRawTransaction(hash);
|
|
77
|
-
|
|
78
|
-
collapseChange;
|
|
79
|
-
let addressBytecode = cashAddressToLockingBytecode(cashaddr);
|
|
80
|
-
if (typeof addressBytecode === "string") throw Error(addressBytecode);
|
|
81
|
-
|
|
82
|
-
let transaction = decodeTransaction(hexToBin(transactionHex));
|
|
83
|
-
if (typeof transaction === "string") throw Error(transaction);
|
|
84
|
-
|
|
85
|
-
let r: TransactionHistoryItemI[] = [];
|
|
86
|
-
r.push(
|
|
87
|
-
...(await getMatchingInputs(
|
|
88
|
-
transaction,
|
|
89
|
-
cashaddr,
|
|
90
|
-
height,
|
|
91
|
-
hash,
|
|
92
|
-
provider,
|
|
93
|
-
collapseChange
|
|
94
|
-
))
|
|
74
|
+
const transaction = transactionCommon as Transaction;
|
|
75
|
+
transaction.blockHeight = tx.height;
|
|
76
|
+
transaction.timestamp = timestampMap[tx.height];
|
|
77
|
+
transaction.hash = tx.tx_hash;
|
|
78
|
+
transaction.size = txHex.length / 2;
|
|
79
|
+
|
|
80
|
+
return transaction;
|
|
81
|
+
})
|
|
95
82
|
);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
83
|
+
|
|
84
|
+
// then load their prevout transactions
|
|
85
|
+
const prevoutTransactionHashes = historicTransactions
|
|
86
|
+
.map((tx) =>
|
|
87
|
+
tx.inputs.map((input) => binToHex(input.outpointTransactionHash))
|
|
88
|
+
)
|
|
89
|
+
.flat()
|
|
90
|
+
.filter((value, index, array) => array.indexOf(value) === index);
|
|
91
|
+
const prevoutTransactionMap = (
|
|
92
|
+
await Promise.all(
|
|
93
|
+
prevoutTransactionHashes.map(async (hash) => {
|
|
94
|
+
const txHex = (await provider.getRawTransaction(hash)) as string;
|
|
95
|
+
|
|
96
|
+
const transaction = decodeTransaction(hexToBin(txHex));
|
|
97
|
+
if (typeof transaction === "string") {
|
|
98
|
+
throw transaction;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return [hash, transaction];
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
).reduce(
|
|
105
|
+
(acc, [hash, transaction]) => ({
|
|
106
|
+
...acc,
|
|
107
|
+
[hash as string]: transaction as TransactionCommon,
|
|
108
|
+
}),
|
|
109
|
+
{} as { [hash: string]: TransactionCommon }
|
|
119
110
|
);
|
|
120
111
|
|
|
121
|
-
|
|
112
|
+
const decoded = decodeCashAddress(address);
|
|
113
|
+
if (typeof decoded === "string") {
|
|
114
|
+
throw decoded;
|
|
115
|
+
}
|
|
122
116
|
|
|
123
|
-
|
|
117
|
+
const addressCache: Record<any, string> = {};
|
|
124
118
|
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
// map decoded transaction data to TransactionHistoryItem
|
|
120
|
+
const historyItems = historicTransactions.map((tx) => {
|
|
121
|
+
const result = {} as TransactionHistoryItem;
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
123
|
+
let inputTotalValue = 0n;
|
|
124
|
+
let outputTotalValue = 0n;
|
|
125
|
+
|
|
126
|
+
result.inputs = tx.inputs.map((input) => {
|
|
127
|
+
const prevoutTx =
|
|
128
|
+
prevoutTransactionMap[binToHex(input.outpointTransactionHash)];
|
|
129
|
+
if (!prevoutTx) {
|
|
130
|
+
throw new Error("Could not find prevout transaction");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const prevoutOutput = prevoutTx.outputs[input.outpointIndex];
|
|
134
|
+
if (!prevoutOutput) {
|
|
135
|
+
throw new Error("Could not find prevout output");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const cached = addressCache[prevoutOutput.lockingBytecode as any];
|
|
139
|
+
let address: string;
|
|
140
|
+
if (!cached) {
|
|
141
|
+
address = lockingBytecodeToCashAddress(
|
|
142
|
+
prevoutOutput.lockingBytecode,
|
|
143
|
+
decoded.prefix as CashAddressNetworkPrefix
|
|
135
144
|
) as string;
|
|
145
|
+
addressCache[prevoutOutput.lockingBytecode as any] = address;
|
|
146
|
+
} else {
|
|
147
|
+
address = cached;
|
|
148
|
+
}
|
|
136
149
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
inputTotalValue += prevoutOutput.valueSatoshis;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
address: address,
|
|
154
|
+
value: Number(prevoutOutput.valueSatoshis),
|
|
155
|
+
token: prevoutOutput.token
|
|
156
|
+
? {
|
|
157
|
+
tokenId: binToHex(prevoutOutput.token.category),
|
|
158
|
+
amount: prevoutOutput.token.amount,
|
|
159
|
+
capability: prevoutOutput.token.nft?.capability
|
|
160
|
+
? prevoutOutput.token.nft.capability
|
|
161
|
+
: undefined,
|
|
162
|
+
commitment: prevoutOutput.token.nft?.capability
|
|
163
|
+
? binToHex(prevoutOutput.token.nft.commitment)
|
|
164
|
+
: undefined,
|
|
165
|
+
}
|
|
166
|
+
: undefined,
|
|
167
|
+
} as InOutput;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
result.outputs = tx.outputs.map((output) => {
|
|
171
|
+
const cached = addressCache[output.lockingBytecode as any];
|
|
172
|
+
let address: string;
|
|
173
|
+
if (!cached) {
|
|
174
|
+
if (output.valueSatoshis === 0n) {
|
|
175
|
+
address = `OP_RETURN: ${binToHex(output.lockingBytecode)}`;
|
|
152
176
|
} else {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
) as string;
|
|
159
|
-
r.push({
|
|
160
|
-
from: from,
|
|
161
|
-
to: to,
|
|
162
|
-
unit: "sat",
|
|
163
|
-
index: idx,
|
|
164
|
-
blockheight: height,
|
|
165
|
-
txn: `${hash}`,
|
|
166
|
-
txId: `${hash}:i:${idx}`,
|
|
167
|
-
value: -Number(output.valueSatoshis),
|
|
168
|
-
fee: fee,
|
|
169
|
-
});
|
|
170
|
-
txIds.push(`${hash}:i:${idx}`);
|
|
171
|
-
}
|
|
177
|
+
address = lockingBytecodeToCashAddress(
|
|
178
|
+
output.lockingBytecode,
|
|
179
|
+
decoded.prefix as CashAddressNetworkPrefix
|
|
180
|
+
) as string;
|
|
181
|
+
addressCache[output.lockingBytecode as any] = address;
|
|
172
182
|
}
|
|
183
|
+
} else {
|
|
184
|
+
address = cached;
|
|
173
185
|
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
186
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
187
|
+
outputTotalValue += output.valueSatoshis;
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
address: address,
|
|
191
|
+
value: Number(output.valueSatoshis),
|
|
192
|
+
token: output.token
|
|
193
|
+
? {
|
|
194
|
+
tokenId: binToHex(output.token.category),
|
|
195
|
+
amount: output.token.amount,
|
|
196
|
+
capability: output.token.nft?.capability
|
|
197
|
+
? output.token.nft.capability
|
|
198
|
+
: undefined,
|
|
199
|
+
commitment: output.token.nft?.capability
|
|
200
|
+
? binToHex(output.token.nft.commitment)
|
|
201
|
+
: undefined,
|
|
202
|
+
}
|
|
203
|
+
: undefined,
|
|
204
|
+
} as InOutput;
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
result.blockHeight = tx.blockHeight;
|
|
208
|
+
result.timestamp = tx.timestamp;
|
|
209
|
+
result.hash = tx.hash;
|
|
210
|
+
result.size = tx.size;
|
|
211
|
+
result.fee = Number(inputTotalValue - outputTotalValue);
|
|
212
|
+
|
|
213
|
+
return result;
|
|
214
|
+
});
|
|
194
215
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
216
|
+
// compute value changes
|
|
217
|
+
historyItems.forEach((tx) => {
|
|
218
|
+
let satoshiBalance = 0;
|
|
219
|
+
const ftTokenBalances: Record<string, bigint> = {};
|
|
220
|
+
const nftTokenBalances: Record<string, bigint> = {};
|
|
221
|
+
|
|
222
|
+
tx.inputs.forEach((input) => {
|
|
223
|
+
if (input.address === address) {
|
|
224
|
+
satoshiBalance -= input.value;
|
|
225
|
+
|
|
226
|
+
if (input.token?.amount) {
|
|
227
|
+
ftTokenBalances[input.token.tokenId] =
|
|
228
|
+
(ftTokenBalances[input.token.tokenId] || BigInt(0)) -
|
|
229
|
+
input.token.amount;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (input.token?.capability) {
|
|
233
|
+
nftTokenBalances[input.token.tokenId] =
|
|
234
|
+
(nftTokenBalances[input.token.tokenId] || BigInt(0)) - 1n;
|
|
207
235
|
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
tx.outputs.forEach((output) => {
|
|
239
|
+
if (output.address === address) {
|
|
240
|
+
satoshiBalance += Number(output.value);
|
|
241
|
+
|
|
242
|
+
if (output.token?.amount) {
|
|
243
|
+
ftTokenBalances[output.token.tokenId] =
|
|
244
|
+
(ftTokenBalances[output.token.tokenId] || BigInt(0)) +
|
|
245
|
+
output.token.amount;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (output.token?.capability) {
|
|
249
|
+
nftTokenBalances[output.token.tokenId] =
|
|
250
|
+
(nftTokenBalances[output.token.tokenId] || BigInt(0)) + 1n;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
tx.valueChange = satoshiBalance;
|
|
256
|
+
tx.tokenAmountChanges = Object.entries(ftTokenBalances).map(
|
|
257
|
+
([tokenId, amount]) => ({
|
|
258
|
+
tokenId,
|
|
259
|
+
amount,
|
|
260
|
+
nftAmount: BigInt(0),
|
|
261
|
+
})
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
for (const [tokenId, nftAmount] of Object.entries(nftTokenBalances)) {
|
|
265
|
+
const tokenChange = tx.tokenAmountChanges.find(
|
|
266
|
+
(tokenChange) => tokenChange.tokenId === tokenId
|
|
267
|
+
);
|
|
268
|
+
if (tokenChange) {
|
|
269
|
+
tokenChange.nftAmount = nftAmount;
|
|
208
270
|
} else {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
.join(";");
|
|
214
|
-
r.push({
|
|
215
|
-
from: from,
|
|
216
|
-
to: cashaddr,
|
|
217
|
-
unit: "sat",
|
|
218
|
-
index: transaction.outputs.indexOf(output),
|
|
219
|
-
blockheight: height,
|
|
220
|
-
txn: `${hash}`,
|
|
221
|
-
txId: `${hash}:o:${transaction.outputs.indexOf(output)}`,
|
|
222
|
-
value: Number(output.valueSatoshis),
|
|
223
|
-
// incoming transactions pay no fee.
|
|
224
|
-
fee: 0,
|
|
271
|
+
tx.tokenAmountChanges.push({
|
|
272
|
+
tokenId,
|
|
273
|
+
amount: BigInt(0),
|
|
274
|
+
nftAmount,
|
|
225
275
|
});
|
|
226
276
|
}
|
|
227
277
|
}
|
|
228
|
-
}
|
|
278
|
+
});
|
|
229
279
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
)
|
|
237
|
-
let inputTransactions: Output[] = [];
|
|
238
|
-
for (let input of transaction.inputs) {
|
|
239
|
-
let inHash = binToHex(input.outpointTransactionHash);
|
|
240
|
-
let transactionHex = await provider.getRawTransaction(inHash);
|
|
241
|
-
let inTransaction = decodeTransaction(hexToBin(transactionHex));
|
|
242
|
-
if (typeof inTransaction === "string") throw Error(inTransaction);
|
|
243
|
-
|
|
244
|
-
inputTransactions.push(inTransaction.outputs[input.outpointIndex]);
|
|
245
|
-
}
|
|
246
|
-
return inputTransactions;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function getFee(
|
|
250
|
-
inputUtxos: Output[],
|
|
251
|
-
utxos: Output[],
|
|
252
|
-
lockingBytecodeHex,
|
|
253
|
-
collapseChange
|
|
254
|
-
) {
|
|
255
|
-
let inValues = 0;
|
|
256
|
-
for (let outpoint of inputUtxos) {
|
|
257
|
-
if (binToHex(outpoint.lockingBytecode)) {
|
|
258
|
-
inValues += Number(outpoint.valueSatoshis);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
280
|
+
// order transactions in a way such that receives are always ordered before sends, per block
|
|
281
|
+
historyItems.sort(
|
|
282
|
+
(a, b) =>
|
|
283
|
+
(a.blockHeight <= 0 || b.blockHeight <= 0
|
|
284
|
+
? a.blockHeight - b.blockHeight
|
|
285
|
+
: b.blockHeight - a.blockHeight) || a.valueChange - b.valueChange
|
|
286
|
+
);
|
|
261
287
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
return fee;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function applyBalance(
|
|
281
|
-
preprocessedTxns: TransactionHistoryItemI[],
|
|
282
|
-
currentBalance: number,
|
|
283
|
-
unit: string,
|
|
284
|
-
factor: number
|
|
285
|
-
): TransactionHistoryItemI[] {
|
|
286
|
-
// balance in satoshis
|
|
287
|
-
let bal = currentBalance;
|
|
288
|
-
|
|
289
|
-
let r: TransactionHistoryItemI[] = [];
|
|
290
|
-
for (let txn of preprocessedTxns) {
|
|
291
|
-
// set the balance to the current balance in the appropriate unit
|
|
292
|
-
txn.balance = bal;
|
|
293
|
-
|
|
294
|
-
// If fee is not defined, configure it to zero.
|
|
295
|
-
txn.fee = txn.fee ? txn.fee : 0;
|
|
296
|
-
|
|
297
|
-
// update the running balance in satoshis for the next record
|
|
298
|
-
// a receiving value is positive, a send is negative
|
|
299
|
-
// The sign is reversed in cronological order from the current balance.
|
|
300
|
-
bal -= txn.value;
|
|
301
|
-
bal += txn.fee;
|
|
302
|
-
|
|
303
|
-
// transform the value of the transaction
|
|
304
|
-
txn.value = txn.value * factor;
|
|
305
|
-
txn.fee = txn.fee! * factor;
|
|
306
|
-
|
|
307
|
-
// If unit is usd, round to two decimal places.
|
|
308
|
-
if (unit.toLowerCase() == "usd") {
|
|
309
|
-
txn.value = floor(txn.value, 2);
|
|
310
|
-
txn.fee = floor(txn.fee, 2);
|
|
311
|
-
}
|
|
288
|
+
// backfill the balances
|
|
289
|
+
let prevBalance = await provider.getBalance(address);
|
|
290
|
+
let prevValueChange = 0;
|
|
291
|
+
historyItems.forEach((tx) => {
|
|
292
|
+
tx.balance = prevBalance - prevValueChange;
|
|
293
|
+
prevBalance = tx.balance;
|
|
294
|
+
prevValueChange = tx.valueChange;
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// convert units if needed
|
|
298
|
+
if (!(unit as string).includes("sat")) {
|
|
299
|
+
for (const tx of historyItems) {
|
|
300
|
+
for (const input of tx.inputs) {
|
|
301
|
+
input.value = await convert(input.value, "sat", unit);
|
|
302
|
+
}
|
|
312
303
|
|
|
313
|
-
|
|
314
|
-
|
|
304
|
+
for (const output of tx.outputs) {
|
|
305
|
+
output.value = await convert(output.value, "sat", unit);
|
|
306
|
+
}
|
|
315
307
|
|
|
316
|
-
|
|
308
|
+
tx.valueChange = await convert(tx.valueChange, "sat", unit);
|
|
309
|
+
tx.balance = await convert(tx.balance, "sat", unit);
|
|
310
|
+
}
|
|
317
311
|
}
|
|
318
|
-
|
|
319
|
-
|
|
312
|
+
|
|
313
|
+
return historyItems;
|
|
314
|
+
};
|
package/src/history/interface.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TokenI } from "../interface.js";
|
|
2
2
|
|
|
3
|
-
export interface
|
|
4
|
-
|
|
5
|
-
from: string;
|
|
6
|
-
unit: UnitEnum;
|
|
7
|
-
index: number;
|
|
8
|
-
blockheight: number;
|
|
9
|
-
txn: string;
|
|
10
|
-
txId: string;
|
|
3
|
+
export interface InOutput {
|
|
4
|
+
address: string;
|
|
11
5
|
value: number;
|
|
12
|
-
|
|
13
|
-
balance?: number;
|
|
6
|
+
token?: TokenI;
|
|
14
7
|
}
|
|
15
8
|
|
|
16
|
-
export interface
|
|
17
|
-
|
|
9
|
+
export interface TransactionHistoryItem {
|
|
10
|
+
inputs: InOutput[];
|
|
11
|
+
outputs: InOutput[];
|
|
12
|
+
blockHeight: number;
|
|
13
|
+
timestamp?: number;
|
|
14
|
+
hash: string;
|
|
15
|
+
size: number;
|
|
16
|
+
fee: number;
|
|
17
|
+
balance: number;
|
|
18
|
+
valueChange: number;
|
|
19
|
+
tokenAmountChanges: {
|
|
20
|
+
tokenId: string;
|
|
21
|
+
amount: bigint;
|
|
22
|
+
nftAmount: bigint;
|
|
23
|
+
}[];
|
|
18
24
|
}
|
package/src/index.ts
CHANGED