Tutorial: Viewing Steemit Account History

in utopian-io •  7 years ago  (edited)

Recently, I and another Steemit user were discussing (I don't remember exactly why) what user accounts were created on '2017-10-11' by anonsteem. Those of you who don't know, anonsteem is an account that will instantly create Steemit accounts for you at the expense of 5 STEEM. This tutorial is not about that though.

As we were asking this, we realized that the API for account history is just a stream of information. It is not easy to filter or query. As we hacked away at it, I realized that this would be a valuable tutorial to give including some lessons learned.

What will you learn

  • Fetching Steemit Account history with nodejs api
  • Steemit api with promises
  • Typescript usage of nodejs API
  • Generator functions and iterating results from asynchronous calls using a for loop
  • Knowledge of what the account history object looks like
  • Manipulating the account history object

Requirements

This tutorial is written for Typescript. To use typescript you really just need nodejs.

Setup

In my Github Repository, there is a module I created for this tutorial for this called steemit-account-history. Please clone this project and open the following file in your IDE: steemit-account-history/src/manage.ts(https://github.com/r351574nc3/steem-bot-examples/blob/topic/steemit-account-history/steemit-account-history/src/manage.ts)

Account History

Account history are transactions that happen to your account like when you change your profile picture or permissions on your keys. Here's an example of what the JSON looks like:

{"trx_id":"092fb94e7ba1b3a66fda64d3fbf4262d5d1c686d","block":16255559,"trx_in_block":0,"op_in_trx":0,"virtual_op":0,"timestamp":"2017-10-12T04:27:03","op":["account_create_with_delegation",{"fee":"6.000 STEEM","delegation":"0.000000 VESTS","creator":"anonsteem","new_account_name":"tapcrypto","owner":{"weight_threshold":1,"account_auths":[],"key_auths":[["STM5ukwG1g39NvtCuG9EfpLWMULFDVCcWUrpsCzufBSebHDmq9RAh",1]]},"active":{"weight_threshold":1,"account_auths":[],"key_auths":[["STM8DFbKF5U2pXScbfm6V6Ro4V52e87Das5RuZtL47JXYcR1cD75d",1]]},"posting":{"weight_threshold":1,"account_auths":[],"key_auths":[["STM7QcUVhUyae3Ec8UFAiqyqFBDb69MX91wn8xmBwk636yxHRCRY9",1]]},"memo_key":"STM5VrJZ4LJ4YZEdkXDk32DqeM7SAV5SFRhfXreUYaL8oYDbHxbBM","json_metadata":"","extensions":[]}]}

getAccountHistory

steem.api.getAccountHistoryAsync(account, from, limit)

My first mistake here was thinking from is a date. It's actually the start index or record number. The reason is the amount of account history can be really HUGE! limit is given to reduce the amount of data which then forces the need to page through data. What you would normally do is start at record 0 and page through the data until you find what you're looking for.

steem.api.getAccountHistoryAsync(account, 0, 100)
steem.api.getAccountHistoryAsync(account, 100, 100)
steem.api.getAccountHistoryAsync(account, 200, 100)

The above shows how multiple calls can be made to page 3 times through 100 account history records (300 in total).

That is pretty much what you would have to do. You can't just retrieve all account history records. Retrieving so much information isn't reliable. In many cases, you will run into a Gateway Timeout. It's best to avoid those; therefore, paging is our only option.

Iterating through every record

Suppose there is some information and you don't know what record you're looking for. How do you query it?

First, what I like to do is find out what the absolute last record is. This is actually not a difficult task. I created a function for this:

async function find_last_record(user: string): Promise<number> {
    let retval = 0;
    await steem.api.getAccountHistoryAsync(user, Number.MAX_SAFE_INTEGER, 1)
        .then((history: any[]) => {
            const record = history[0];
            retval = record[0];
        });
    return retval;
}

You can see I used a really huge number (Number.MAX_SAFE_INTEGER) .

Now, I can safely page through the data knowing just how far I need to go.

    for (let start = step; start < end; start = start + step) {
        await steem.api.getAccountHistoryAsync(user, start, step)

end represents that upper bound I discovered by grabbing the last record. Now, what I need are all the records after 2017-10-11. To get these, I'm going go iterate through all the records and skip them until I arrive at the first record I care about. Here's my full loop.

    for (let start = step; start < end; start = start + step) {
        await steem.api.getAccountHistoryAsync(user, start, step)
            .each((history: any[]) => {
                if (!history || history.length < 1) {
                    return;
                }
                const record = history[1];
                const on_date = moment(date);
                const ts = moment(record.timestamp);
                if (ts.startOf("day").isAfter(on_date.startOf("day"))) {
                    retval.push(record);
                }
            });
    }

You can see that I check for ts.startOf("day").isAfter(on_date.startOf("day") to determine if the record is important or not. If it is, I push it onto a buffer.

I then stuff all this goodness into a generator function

async function* seek(user: string, date: string): any {
    const step = 1000;
    const retval: any[] = [];
    let end = 0;
    await find_last_record(user).then((result: number) => {
        end = result;
    });

    for (let start = step; start < end; start = start + step) {
        await steem.api.getAccountHistoryAsync(user, start, step)
            .each((history: any[]) => {
                if (!history || history.length < 1) {
                    return;
                }
                const record = history[1];
                const on_date = moment(date);
                const ts = moment(record.timestamp);
                if (ts.startOf("day").isAfter(on_date.startOf("day"))) {
                    retval.push(record);
                }
            });
    }
    yield* retval;
}

The reason I did this is because I want to be able to loop through my account history records with a for loop.

async function main() {
    const result = seek("anonsteem", "2017-10-11");
    for await (const item of result) {
        console.log("Record %s", JSON.stringify({ operation: item.op[0], timestamp: item.timestamp }));
    }
}

I slimmed down the records with { operation: item.op[0], timestamp: item.timestamp } because I just wanted to know what the operation was. I could always add more details if I want later. My results end up looking like this:

Record {"operation":"transfer","timestamp":"2017-10-12T04:02:54"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T04:03:09"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T04:20:21"}
Record {"operation":"transfer","timestamp":"2017-10-12T04:25:42"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T04:27:03"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T05:03:03"}
Record {"operation":"transfer","timestamp":"2017-10-12T06:43:45"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T06:44:09"}
Record {"operation":"transfer","timestamp":"2017-10-12T07:17:03"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T07:17:57"}
Record {"operation":"transfer","timestamp":"2017-10-12T10:15:00"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T10:16:15"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T10:29:27"}
Record {"operation":"account_create_with_delegation","timestamp":"2017-10-12T14:47:42"}

Thank you


I hope you enjoyed this and that it was helpful to you.

Other Tutorials in this Series



Posted on Utopian.io - Rewarding Open Source Contributors

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.

[utopian-moderator]

Hey @yokunjon, I just gave you a tip for your hard work on moderation. Upvote this comment to support the utopian moderators and increase your future rewards!

Moar like this!

You have mad coding skillz! Can you do a tutorial on changing your recovery account or how to create a telegram bot?

Good info, as always! Have you thought about using a GIST to show some of the code snipets for people that don't want to go through and fork the project?

This for utopian, and utopian requires an actual repository instead of a gist, so...

  ·  7 years ago (edited)

The repo area of Utopian is meant for the project used in the topic, not your own codes. So, can you change your repo to steem-js, please?

Here is the link: https://github.com/steemit/steem-js

When you change, don't forget to reply this message, so I can remember easily.

Changed.

Ah, ok. Thanks for clarifying that. I'll change it.

Hey @r351574nc3 I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x

Hey Leo, I just found your tutorial while investigating a strange thing when working with the Steem account history.

I am able to load the first 1000 records and then load another 1000, but at one point I do not get the complete data back but this:

…
2707    vote
2706    transfer
2705    curation_reward
0   account_create_with_delegation

The records for 1 through 2704 are missing. Is this a limitation of the SteemJS lib? Did you run into a similar thing when trying to retrieve the complete account history for a user?

I was able to get all the records I was looking for, so I didn't run into this. Can you point me to code? Data you're working with? I'd like to reproduce it and have a look.

Well, I'm doing nothing special. This is the call:

const accountHistoryData = await steem.api.getAccountHistoryAsync('cutemachine', -1, 6000)
console.log(accountHistoryData)

This should give me the complete history for my account, as I only have ~4000 records. Is it possible that I need to connect to a special server?

Oh, that clears things up. Thanks.

Is it possible that I need to connect to a special server?

It is. Only full nodes contain account history. They are pretty few.

Good Morning @r351574nc3, I find the need to retrieve several posts from my account, posted 9 months ago.
It was a serial post of non fiction, words my mother wrote about her
early life and my family.
Deeply personal stuff, which I put a GREAT DEAL of effort into crafting a quality storyline. Complete with photos etc.
I still have the story of course because mama wrote it, but I can't make my profile feed go back past 5 months.
I CAN find the links in search, but not the very first one.
I don't understand all this github mishmash, json this blah blah.

Would you please do me the great favor of helping me retrieve these posts?
Please?