This repository has been archived by the owner on Oct 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
284 lines (244 loc) · 13.8 KB
/
index.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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
require('dotenv').config();
const redis = require("redis");
const bluebird = require("bluebird");
bluebird.promisifyAll(redis);
const LivepeerSDK = require('@livepeer/sdk');
const Web3 = require('web3');
const SolidityCoder = require("./../web3.js/lib/solidity/coder.js");
const compileTxnHistory = require('./combine-txns-history.js');
const web3 = new Web3(new Web3.providers.HttpProvider('https://eth.bisontrailscomputing.com'));
const Json2csvParser = require('json2csv').Parser;
const fs = require("fs");
const RDS_TXN_PRE = 'eth_txn_receipt.';
const RDS_BLOCK_PRE = 'eth_block.';
const LIVEPEER_CONTRACT = '0x511Bc4556D823Ae99630aE8de28b9B80Df90eA2e';
const VALIDATOR_ADDRESS ='0x50d69f8253685999b4c74a67ccb3d240e2a56ed6'.toLowerCase();
const TRANSFER_LOG_FN_CALL_SIG = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
const REWARD_LOG_FN_CALL_SIG = '0x619caafabdd75649b302ba8419e48cccf64f37f1983ac4727cfb38b57703ffc9'.toLowerCase();
const BOND_LOG_FN_CALL_SIG = '0x926f98e4b543897a75b3e34b7494ba68a47829d3aa39ffd9c478ccc51bfbfb44';
const REBOND_FN_CALL_SIG = '0x9f5b64cc71e1e26ff178caaa7877a04d8ce66fde989251870e80e6fbee690c17';
const UNBOND_FN_CALL_SIG = '0x2d5d98d189bee5496a08db2a5948cb7e5e786f09d17d0c3f228eb41776c24a06';
const BOND_FN_CALL_SIG_2 = '0xe5917769f276ddca9f2ee7c6b0b33e1d1e1b61008010ce622c632dd20d168a23'.toLowerCase();
const E_REWARD = 'REWARD';
const E_BOND = 'BOND';
const E_UNBOND = 'UNBOND';
const E_REBOND = 'REBOND';
const E_MOVE_BOND = 'MOVE_BOND';
const BN_DIVIDE_BY = new web3.utils.BN(100000000000000);
const DIVIDE_BY_2 = 100000000;
const redisClient = redis.createClient();
const main = async () => {
//const livepeerSdk = await LivepeerSDK.default();
// const { rpc } = livepeerSdk;
// const tokens = await rpc.getTokenTotalSupply();
//
// console.log('total supply', tokens);
//
// const status = await rpc.getDelegatorStatus('');
// console.log('status', status);
//
// const delegator = await rpc.getDelegator('');
// console.log('delegator', delegator);
//
// const transcoder = await rpc.getTranscoder('');
// console.log('transcoder', transcoder);
const acctTxns = await compileTxnHistory();
const bondedAccounts = [];
const bondedAccountsAmount = {};
const transcoderTracker = [];
const data = {
totalBonded: 0.0,
totalBondAmount: []
};
for(let i=0; i<acctTxns.length; i++) {
const txHash = acctTxns[i].Txhash;
// const txHash = '0xc738398fed8efa17ee69aca44c5bfa08c2fc85aeb8664fe6723f169f30b77407';
// const txHash = '0xb1dab7ebb7c8e1fe8adf35671270b5f92dab40bc34dce2c7415ee064f40da1de';
// console.log('fetching txn', txHash);
try {
// const txn = await web3.eth.getTransaction(txHash);
if (txHash) {
const txnReceipt = await getEthTransactionReceipt(txHash);
const fromAddress = txnReceipt.from;
const blockNumber = txnReceipt.blockNumber;
const info = {};
for (let j = 0; j < txnReceipt.logs.length; j++) {
const topics = txnReceipt.logs[j].topics;
//TODO: claimEarnings
//TODO: how to see if bonder switched transcoders... (this might require tracking the address that has bonded)
if (topics[0].toString().toLowerCase() == REWARD_LOG_FN_CALL_SIG) {
const rewardFloat = lptHexToNum(txnReceipt.logs[j].data) / 10000000000.0;
if (fromAddress == VALIDATOR_ADDRESS) {
const blockInfo = await getEthBlock(blockNumber);
const timestamp = blockInfo.timestamp;
// console.log('[reward] ' + fromAddress + ' called Reward, received ' + rewardFloat + ' LPT at block number ' + blockNumber + ' at ' + timestamp) + ' [' + txHash + ']';
if (rewardFloat) {
data.totalBonded += rewardFloat;
}
await trackEvent(E_REWARD, fromAddress, rewardFloat, blockNumber, transcoderTracker);
}
// console.log('for address', SolidityCoder.decodeParam('address', topics[1]));
} else if (topics[0].toString().toLowerCase() == BOND_FN_CALL_SIG_2) {
const stripped0x = topics[1].substr(2, topics[1].length - 2);
const transcoderAddress = SolidityCoder.decodeParam('address', stripped0x);
const amountA = txnReceipt.logs[j].data.substr(2, 66);
const amountB = txnReceipt.logs[j].data.substr(66, 64);
const newBondAmount = lptBNToFloat(SolidityCoder.decodeParam('uint', amountA)) / 10000000000.0;
const newTotalBondAmount = lptBNToFloat(SolidityCoder.decodeParam('uint', amountB)) / 10000000000.0;
let addedBond = 0.0;
if (newBondAmount) {
addedBond = newBondAmount;
} else if (bondedAccountsAmount[fromAddress]) {
addedBond = bondedAccountsAmount[fromAddress];
}
if (transcoderAddress.toLowerCase() == VALIDATOR_ADDRESS) {
data.totalBondAmount.push(newTotalBondAmount);
data.totalBonded += addedBond;
console.log('[bond] ' + fromAddress + ' bonded ' + addedBond + ' LPT to ' + transcoderAddress + ' total: ' + newTotalBondAmount + ' LPT' + ' [' + txHash + ']');
await trackEvent(E_BOND, fromAddress, addedBond, blockNumber, transcoderTracker);
addToBondedAccounts(fromAddress, newBondAmount, bondedAccounts, bondedAccountsAmount);
} else if (transcoderAddress.toLowerCase() != VALIDATOR_ADDRESS && bondedAccounts.indexOf(fromAddress) > -1) {
const blockInfo = await getEthBlock(blockNumber);
console.log(fromAddress + ' went somewhere else!!! ' + txHash);
const amountMoving = bondedAccountsAmount[fromAddress];
data.totalBonded -= amountMoving;
bondedAccounts.splice(bondedAccounts.indexOf(fromAddress), 1);
await trackEvent(E_MOVE_BOND, fromAddress, -amountMoving, blockNumber, transcoderTracker);
}
addToBondedAccountsAmount(fromAddress, newBondAmount, bondedAccountsAmount);
} else if (topics[0].toString().toLowerCase() == REBOND_FN_CALL_SIG) {
const stripped0x = topics[1].substr(2, topics[1].length - 2);
const transcoderAddress = SolidityCoder.decodeParam('address', stripped0x);
if (transcoderAddress.toLowerCase() == VALIDATOR_ADDRESS) {
const amountHex = txnReceipt.logs[j].data.substr(66, 64);
const amount = lptBNToFloat(SolidityCoder.decodeParam('uint', amountHex)) / 10000000000.0;
console.log('[rebond] ' + fromAddress + ' rebonded ' + amount + ' LPT to ' + transcoderAddress + ' [' + txHash + ']');
addToBondedAccounts(fromAddress, amount, bondedAccounts, bondedAccountsAmount);
if (amount) {
data.totalBonded += amount;
await trackEvent(E_REBOND, fromAddress, amount, blockNumber, transcoderTracker);
}
}
} else if (topics[0].toString().toLowerCase() == UNBOND_FN_CALL_SIG) {
//todo: figure out how withdraw round affects things here
const stripped0x = topics[1].substr(2, topics[1].length - 2);
const transcoderAddress = SolidityCoder.decodeParam('address', stripped0x);
if (bondedAccounts.indexOf(fromAddress) > -1) {
if (transcoderAddress == VALIDATOR_ADDRESS) {
const amountHex = txnReceipt.logs[j].data.substr(66, 64);
const amount = lptBNToFloat(SolidityCoder.decodeParam('uint', amountHex)) / 10000000000.0;
console.log('[unbond] ' + fromAddress + ' unbonded ' + amount + ' LPT from ' + transcoderAddress + ' [' + txHash + ']');
bondedAccounts.splice(bondedAccounts.indexOf(fromAddress), 1);
if (amount) {
data.totalBonded -= amount;
}
await trackEvent(E_UNBOND, fromAddress, -amount, blockNumber, transcoderTracker);
}
}
} else if (topics[0].toString().toLowerCase() == BOND_LOG_FN_CALL_SIG) {
const stripped0x = topics[1].substr(2, topics[1].length - 2);
const transcoderAddress = SolidityCoder.decodeParam('address', stripped0x);
if (transcoderAddress.toLowerCase() == VALIDATOR_ADDRESS) {
let addedBond = 0.0;
if (info.transferAmount) {
addedBond += info.transferAmount;
} else if (bondedAccountsAmount[fromAddress]) {
addedBond += bondedAccountsAmount[fromAddress];
}
data.totalBonded += addedBond;
await trackEvent(E_BOND, fromAddress, addedBond, blockNumber, transcoderTracker);
addToBondedAccounts(fromAddress, info.transferAmount, bondedAccounts, bondedAccountsAmount);
console.log('[bond] ' + fromAddress + ' bonded ' + addedBond + ' LPT to ' + transcoderAddress + ' [' + txHash + ']');
} else if (transcoderAddress.toLowerCase() != VALIDATOR_ADDRESS && bondedAccounts.indexOf(fromAddress) > -1) {
console.log(fromAddress + ' went somewhere else!!! ' + txHash);
const amountMoving = bondedAccountsAmount[fromAddress];
data.totalBonded -= amountMoving;
await trackEvent(E_MOVE_BOND, fromAddress, -amountMoving, blockNumber, transcoderTracker);
}
addToBondedAccountsAmount(fromAddress, info.transferAmount, bondedAccountsAmount);
} else if (topics[0].toString().toLowerCase() == TRANSFER_LOG_FN_CALL_SIG) {
// const stripped0x = topics[1].substr(2, topics[1].length - 2);
// const transcoderAddress = SolidityCoder.decodeParam('address', stripped0x);
info.transferAmount = lptHexToNum(txnReceipt.logs[j].data) / 10000000000.0;
}
}
}
} catch (ex) {
console.log('faild: ' + ex.toString().substr(0, 30));
console.log(ex);
console.log(txHash);
if (ex.toString().indexOf("Invalid JSON RPC") > -1) {
//do a pause in case we are being rate limited
await timeout(1000);
}
}
}
console.log('Transcoder ' + VALIDATOR_ADDRESS + ' has ' + data.totalBonded + ' LPT bonded.');
const fields = ['type', 'address', 'amount', 'date', 'time'];
const opts = { fields };
//write csv file
try {
const parser = new Json2csvParser(opts);
const csv = parser.parse(transcoderTracker);
fs.writeFile("./output/" + VALIDATOR_ADDRESS + ".csv", csv, (err) => {
if (err) {
console.error(err);
return;
};
console.log("File has been created");
});
} catch (err) {
console.error(err);
}
};
const timeout = (ms) => new Promise(res => setTimeout(res, ms));
const getEthTransactionReceipt = async (txnHash) => {
const key = RDS_TXN_PRE + txnHash;
const receiptRds = await redisClient.getAsync(key);
if (receiptRds != null && receiptRds !== 'null') {
return JSON.parse(receiptRds);
}
const receipt = await web3.eth.getTransactionReceipt(txnHash);
redisClient.set(key, JSON.stringify(receipt));
return receipt;
};
const getEthBlock = async (blockNum) => {
const key = RDS_BLOCK_PRE + blockNum;
const blockRds = await redisClient.getAsync(key);
if (blockRds != null) {
return JSON.parse(blockRds);
}
const block = await web3.eth.getBlock(blockNum);
redisClient.set(key, JSON.stringify(block));
return block;
};
const lptHexToNum = (hex) => {
const reward = new web3.utils.BN(web3.utils.hexToNumberString(hex));
return lptBNToFloat(reward);
};
const lptBNToFloat = (bn) => {
const tmpBN = bn.div(BN_DIVIDE_BY);
return parseFloat(bn.toString())/(DIVIDE_BY_2);
}
const addToBondedAccounts = (address, amount, bondedAccounts) => {
if (bondedAccounts.indexOf(address) == -1) {
bondedAccounts.push(address);
}
}
const trackEvent = async (type, address, amount, blockNumber, tracker) => {
const blockInfo = await getEthBlock(blockNumber);
tracker.push({
type: type,
address: address,
amount: amount,
date: new Date(blockInfo.timestamp*1000),
time: blockInfo.timestamp
});
}
const addToBondedAccountsAmount = (address, amount, bondedAccountsAmount) => {
if (!bondedAccountsAmount[address]) {
bondedAccountsAmount[address] = 0.0;
}
if (amount) bondedAccountsAmount[address] += amount;
}
main();