restake 2.0.1 → 2.2.0
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.js +98 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29,6 +29,9 @@ const postageBatchId = cafe_utility_1.Arrays.requireStringArgument(process_1.arg
|
|
|
29
29
|
const externalWallet = cafe_utility_1.Arrays.requireStringArgument(process_1.argv, 'external-wallet');
|
|
30
30
|
const privateKeysPath = cafe_utility_1.Arrays.requireStringArgument(process_1.argv, 'private-keys-path');
|
|
31
31
|
const jsonRpcUrl = cafe_utility_1.Arrays.requireStringArgument(process_1.argv, 'json-rpc-url');
|
|
32
|
+
const telegramToken = cafe_utility_1.Arrays.requireStringArgument(process_1.argv, 'telegram-token');
|
|
33
|
+
const telegramChatId = cafe_utility_1.Arrays.requireStringArgument(process_1.argv, 'telegram-chat-id');
|
|
34
|
+
const BASE_PORT = 1633;
|
|
32
35
|
const privateKeys = (0, fs_1.readFileSync)(privateKeysPath, 'utf-8').split('\n').filter(Boolean);
|
|
33
36
|
if (n !== privateKeys.length) {
|
|
34
37
|
console.error(`Expected ${n} private keys, but got ${privateKeys.length}`);
|
|
@@ -42,7 +45,7 @@ async function main() {
|
|
|
42
45
|
async function validatePrivateKeys() {
|
|
43
46
|
for (let i = 0; i < n; i++) {
|
|
44
47
|
const privateKey = privateKeys[i];
|
|
45
|
-
const port =
|
|
48
|
+
const port = BASE_PORT + i;
|
|
46
49
|
const bee = new bee_js_1.Bee(`http://localhost:${port}`);
|
|
47
50
|
const addresses = await bee.getNodeAddresses();
|
|
48
51
|
const address = (0, accounts_1.privateKeyToAccount)(ensure0x(privateKey)).address;
|
|
@@ -54,34 +57,92 @@ async function validatePrivateKeys() {
|
|
|
54
57
|
}
|
|
55
58
|
function runLoop() {
|
|
56
59
|
cafe_utility_1.System.forever(async () => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (wallet.bzzBalance.lt(bzz)) {
|
|
61
|
-
console.log(`:${port} balance is ${wallet.bzzBalance.toDecimalString()} BZZ, skipping`);
|
|
60
|
+
const eligiblePorts = await findEligiblePorts();
|
|
61
|
+
if (eligiblePorts.length === 0) {
|
|
62
|
+
console.log('no eligible nodes found, skipping');
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
const port = cafe_utility_1.Arrays.pick(eligiblePorts);
|
|
66
|
+
const bee = new bee_js_1.Bee(`http://localhost:${port}`);
|
|
67
|
+
if (cafe_utility_1.Random.chance(1 / 4)) {
|
|
68
|
+
await runAction(port, `stake ${bzz.toDecimalString()} BZZ`, () => stake(bee, port));
|
|
69
|
+
}
|
|
70
|
+
else if (cafe_utility_1.Random.chance(1 / 3)) {
|
|
71
|
+
await runAction(port, `transfer ${bzz.toDecimalString()} BZZ to external wallet`, () => transfer(port));
|
|
67
72
|
}
|
|
68
73
|
else if (cafe_utility_1.Random.chance(1 / 2)) {
|
|
69
|
-
|
|
70
|
-
await transferBZZ(privateKeys[port - 1633], externalWallet, bzz);
|
|
74
|
+
await runAction(port, `top up postage batch ${postageBatchId}`, () => topup(bee, port));
|
|
71
75
|
}
|
|
72
76
|
else {
|
|
73
|
-
|
|
74
|
-
const postageBatch = postageBatches.find(batch => batch.batchID.toString() === postageBatchId);
|
|
75
|
-
if (!postageBatch) {
|
|
76
|
-
console.error(`Postage batch with ID ${postageBatchId} not found`);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const { amount } = await bee.calculateTopUpForBzz(postageBatch.depth, bzz);
|
|
80
|
-
console.log(`:${port} topping up postage batch ${postageBatchId} with ${amount} amount`);
|
|
81
|
-
await bee.topUpBatch(postageBatch.batchID, amount);
|
|
77
|
+
await runAction(port, `aid weakest node with ${bzz.toDecimalString()} BZZ`, () => aid(port));
|
|
82
78
|
}
|
|
83
79
|
}, sleep, console.error);
|
|
84
80
|
}
|
|
81
|
+
async function findEligiblePorts() {
|
|
82
|
+
const eligible = [];
|
|
83
|
+
for (let i = 0; i < n; i++) {
|
|
84
|
+
const port = BASE_PORT + i;
|
|
85
|
+
const bee = new bee_js_1.Bee(`http://localhost:${port}`);
|
|
86
|
+
const [wallet, redistribution] = await Promise.all([bee.getWalletBalance(), bee.getRedistributionState()]);
|
|
87
|
+
if (!redistribution.isFrozen && wallet.bzzBalance.gte(bzz)) {
|
|
88
|
+
eligible.push(port);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return eligible;
|
|
92
|
+
}
|
|
93
|
+
async function runAction(port, description, action) {
|
|
94
|
+
try {
|
|
95
|
+
await action();
|
|
96
|
+
await sendTelegramMessage(`Port ${port}: ${description} succeeded`);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
await sendTelegramMessage(`Port ${port}: ${description} failed: ${error}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function stake(bee, port) {
|
|
103
|
+
console.log(`:${port} depositing ${bzz.toDecimalString()} BZZ as stake`);
|
|
104
|
+
await bee.depositStake(bzz);
|
|
105
|
+
}
|
|
106
|
+
async function transfer(port) {
|
|
107
|
+
console.log(`:${port} transferring ${bzz.toDecimalString()} BZZ to external wallet`);
|
|
108
|
+
await transferBZZ(privateKeys[port - BASE_PORT], externalWallet, bzz);
|
|
109
|
+
}
|
|
110
|
+
async function topup(bee, port) {
|
|
111
|
+
const postageBatches = await bee.getGlobalPostageBatches();
|
|
112
|
+
const postageBatch = postageBatches.find(batch => batch.batchID.toString() === postageBatchId);
|
|
113
|
+
if (!postageBatch) {
|
|
114
|
+
console.error(`Postage batch with ID ${postageBatchId} not found`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { amount } = await bee.calculateTopUpForBzz(postageBatch.depth, bzz);
|
|
118
|
+
console.log(`:${port} topping up postage batch ${postageBatchId} with ${amount} amount`);
|
|
119
|
+
await bee.topUpBatch(postageBatch.batchID, amount);
|
|
120
|
+
}
|
|
121
|
+
async function aid(port) {
|
|
122
|
+
const weakestAddress = await findWeakestNodeAddress();
|
|
123
|
+
const currentAddress = (0, accounts_1.privateKeyToAccount)(ensure0x(privateKeys[port - BASE_PORT])).address;
|
|
124
|
+
if (weakestAddress && weakestAddress.toLowerCase() !== currentAddress.toLowerCase()) {
|
|
125
|
+
console.log(`:${port} aiding weakest node ${weakestAddress} with ${bzz.toDecimalString()} BZZ`);
|
|
126
|
+
await transferBZZ(privateKeys[port - BASE_PORT], weakestAddress, bzz);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.log(`:${port} weakest node is self, skipping aid`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function findWeakestNodeAddress() {
|
|
133
|
+
let minStake = null;
|
|
134
|
+
let weakestAddress = null;
|
|
135
|
+
for (let i = 0; i < n; i++) {
|
|
136
|
+
const port = BASE_PORT + i;
|
|
137
|
+
const bee = new bee_js_1.Bee(`http://localhost:${port}`);
|
|
138
|
+
const stake = await bee.getStake();
|
|
139
|
+
if (minStake === null || stake.lt(minStake)) {
|
|
140
|
+
minStake = stake;
|
|
141
|
+
weakestAddress = (0, accounts_1.privateKeyToAccount)(ensure0x(privateKeys[i])).address;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return weakestAddress;
|
|
145
|
+
}
|
|
85
146
|
async function transferBZZ(privateKey, targetAddress, amount) {
|
|
86
147
|
const account = (0, accounts_1.privateKeyToAccount)(ensure0x(privateKey));
|
|
87
148
|
const client = (0, viem_1.createWalletClient)({
|
|
@@ -107,11 +168,12 @@ async function getGasPrice() {
|
|
|
107
168
|
return bee_js_1.DAI.fromWei(BigInt(price));
|
|
108
169
|
}
|
|
109
170
|
async function getTransactionCount(address) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
171
|
+
const payload = {
|
|
172
|
+
jsonrpc: '2.0',
|
|
173
|
+
id: 1,
|
|
174
|
+
method: 'eth_getTransactionCount',
|
|
175
|
+
params: [address.toLowerCase(), 'latest']
|
|
176
|
+
};
|
|
115
177
|
const count = await fetchJsonRpcHexString(payload);
|
|
116
178
|
return parseInt(count, 16);
|
|
117
179
|
}
|
|
@@ -128,5 +190,16 @@ async function fetchJsonRpcHexString(payload) {
|
|
|
128
190
|
function ensure0x(value) {
|
|
129
191
|
return value.startsWith('0x') ? value : `0x${value}`;
|
|
130
192
|
}
|
|
193
|
+
async function sendTelegramMessage(text) {
|
|
194
|
+
console.log('Sending Telegram message:', text);
|
|
195
|
+
await fetch(`https://api.telegram.org/bot${telegramToken}/sendMessage`, {
|
|
196
|
+
method: 'POST',
|
|
197
|
+
headers: { 'Content-Type': 'application/json' },
|
|
198
|
+
body: JSON.stringify({ chat_id: telegramChatId, text }),
|
|
199
|
+
signal: AbortSignal.timeout(cafe_utility_1.Dates.seconds(10))
|
|
200
|
+
}).catch(error => {
|
|
201
|
+
console.error('Failed to send Telegram message:', error);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
131
204
|
process.on('unhandledRejection', console.error);
|
|
132
205
|
process.on('uncaughtException', console.error);
|