Niy mŏm pojyncio co robia cz. 1

in #hive-1343824 months ago

signal-2024-07-28-212946.jpeg

  • Otyły Panie, mam dobrą i złą wiadomość.
  • Zacznij od złej.
  • Nie działa.
  • A dobra?
  • Zbudowaliśmy go.

Miałem plan, sprytny, dzięki któremu miałem uczynić swoje życie nieco łatwiejszym. Pierwszy zakładał dostęp do danych archiwalnych Hive. @gtg uświadomił mnie, że łańcuch bloków Hive takiej informacji nie przechowuje. Trzeba to zrobić za pomocą zawsze skutecznej analizy wstecznej, ale jest też coś takiego jak Balance Tracker. Dopiero po kilkunastu godzinach prób ogarnięcia tego okazało się (mi się okazało), że trzeba do tego postawić instancję HAF, ale ta - jak rozumiem - wymaga 4 TB pamięci, którymi nie dysponuję. Gandalf polecił mi raczej sprawdzić API, które mają tego balance trackera. Obsługa swaggera też mnie przerosła. Z pomocą chata GPT próbuję
uzyskać dane wsteczne (aktualnie) za pomocą zapytania w JavaScript. Ale tu napotykam nowe problemy. Poczekam do wtorku w nadziei 😂
Czy ktoś z Was ma może pomysł jak mogę to zrobić? (na samym dole wklejam dla zainteresowanych kod).

Niejako powiązany z pierwszym pomysłem jest drugi - chciałem jakoś zautomatyzować jakoś proces pozyskiwania Hive Power do raportów. Z racji tego, że Hive SQL korzysta z Vests (dynamicznie przeliczanych na HP), czego nie potrafię przeskoczyć, również z powodu ilości zapytań, które można kierować do bazy - pomyślałem, że pójdę na skróty i zrobię scrape ze strony wallet.hive.blog za pomocą Pythona. Ku mojemu zdziwieniu, zadziałało. Mówię super. Ale ręczna weryfikacja pokazuje, że dane są błędne. Na to wszystko nakładają się ograniczenia ChataGPT (który mimo wszystko oferuje, że będzie mi pomagać xD). Musiałem wprowadzić regułę, w której scrapper ignoruje pierwszą wartość wyrażoną w Hive pomimo tego, że dokładnej pozycji konkretnego <div>. Nie rozumiem dlaczego, ale działa. Pierwszy realny test juz za kilka dni.

image.png

pozwolę sobie jeszcze zawołać @szejq i @engrave

const hive = require('@hiveio/hive-js');
const ProgressBar = require('progress');

async function getAccountHistory(account, start, limit, retries = 3) {
  return new Promise((resolve, reject) => {
    hive.api.getAccountHistory(account, start, limit, (err, result) => {
      if (err) {
        if (retries > 0) {
          console.log(`Błąd połączenia, ponawianie próby... Pozostało prób: ${retries}`);
          setTimeout(() => {
            resolve(getAccountHistory(account, start, limit, retries - 1));
          }, 2000); // Poczekaj 2 sekundy przed ponowieniem próby
        } else {
          reject(err);
        }
      } else {
        resolve(result);
      }
    });
  });
}

async function processHistoryBatch(username, start, end_date) {
  let history = [];
  let last_index = start;
  while (true) {
    const batch = await getAccountHistory(username, last_index, 1000);
    if (!batch.length) break;
    history = history.concat(batch);
    last_index = batch[batch.length - 1][0];
    if (new Date(batch[batch.length - 1][1].timestamp) < new Date(end_date)) break;
  }
  return history;
}

async function calculateBalances(username, history) {
  let hbd_balance = 0;
  let savings_hbd_balance = 0;

  for (const operation of history) {
    const [index, op] = operation;
    const op_type = op.op[0];
    const op_data = op.op[1];

    if (op_type === 'transfer') {
      const [amount, asset] = op_data.amount.split(' ');
      const amount_float = parseFloat(amount);
      if (asset === 'HBD') {
        if (op_data.to === username) {
          hbd_balance += amount_float;
        } else if (op_data.from === username) {
          hbd_balance -= amount_float;
        }
      }
    }

    if (op_type === 'transfer_to_savings') {
      const [amount, asset] = op_data.amount.split(' ');
      const amount_float = parseFloat(amount);
      if (asset === 'HBD' && op_data.from === username) {
        hbd_balance -= amount_float;
        savings_hbd_balance += amount_float;
      }
    }

    if (op_type === 'transfer_from_savings') {
      const [amount, asset] = op_data.amount.split(' ');
      const amount_float = parseFloat(amount);
      if (asset === 'HBD' && op_data.to === username) {
        hbd_balance += amount_float;
        savings_hbd_balance -= amount_float;
      }
    }
  }

  return { hbd_balance, savings_hbd_balance };
}

async function getMonthEndBalances(username, start_date, end_date) {
  let current_date = new Date(start_date);
  const end_date_obj = new Date(end_date);
  const balances = [];

  const totalMonths = (end_date_obj.getFullYear() - current_date.getFullYear()) * 12 + (end_date_obj.getMonth() - current_date.getMonth()) + 1;
  const bar = new ProgressBar('[:bar] :current/:total :percent :etas', { total: totalMonths });

  while (current_date <= end_date_obj) {
    const next_month = new Date(current_date.getFullYear(), current_date.getMonth() + 1, 1);
    const last_day_of_month = new Date(next_month - 1);

    const history = await processHistoryBatch(username, -1, last_day_of_month);

    const { hbd_balance, savings_hbd_balance } = await calculateBalances(username, history);

    balances.push({
      date: last_day_of_month.toISOString().split('T')[0],
      hbd_balance,
      savings_hbd_balance
    });

    bar.tick();
    current_date = next_month;
  }

  return balances;
}

(async () => {
  const username = 'jocieprosza';
  const start_date = '2024-01-01';
  const end_date = '2024-06-30';

  const balances = await getMonthEndBalances(username, start_date, end_date);

  for (const balance of balances) {
    console.log(balance);
  }
})();


Sort:  

Możesz napisać jakie dane konkretnie potrzebujesz? Stan HP dla określonych kont na koniec każdego miesiąca, żeby potem policzyć przyrost/narysować wykres?

Dokładnie chodzi mi o stan HBD i HBD w savings, tak, dla wizualizacji itp.

O panie, kto to panu to tak ... ;-)

Jest teraz dostępna instancja swaggera, która jest zajawką nadchodzących zmian w API: https://api.syncad.com/ ale to świeżo udostępnione przez @blocktrades a w danych zwracanych przez balance tracker znalazłem nieprawidłowości więc jeszcze chwilę toto zajmie.

O panie, kto to panu to tak ... ;-)

@tipu curate 8

Wśród wywołanych do tablicy czuję się jak przedszkolak na lekcji ostatniej klasy podstawówki 😄
Co do pobierania danych wstecznych (historycznych) - nie mam z tym doświadczenia.
Myślę, że bieżące (aktualne) dane można wyciągnąć z Hive SQL zaledwie kilkoma zapytaniami. Za tydzień wrócę z urlopu (więc za późno na pobranie danych z przełomu miesiąca) to mogę Ci spróbować zrobić jakiś tutorial, chyba że do tego czasu już Witness'i pomogą 😊

No tak, można wyciągnąć, ale SQL nie podaje HP tylko Vests, a ilekroć próbowałem dodać, żeby mi samo przeliczało, to kończyło się banem na zapytania. Więc scraping jest rozwiązaniem mało mądrym, ale już działa. A co do danych przeszłych to zobaczymy co się uda załatwić

Żeby zamienić na HP możesz sobie do Excel wyeksportować userów i ich Vest. Ten Vest możesz później w osobnej kolumnie przeliczyć funkcją logiczną w Excel tworząc sobie wzór (podkładając pobrane jednokrotnie aktualne globalne dane).
Wzór z tutorialem tak na szybko Ci znalazłem tutaj:
https://peakd.com/@behiver/the-math-behind-vests-and-hive-power-on-the-hive-blockchain

No nie wpadłem na to, żeby przeliczać to lokalnie, ale w sumie scraping też chyba będzie działać