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
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
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!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Moar like this!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
You have mad coding skillz! Can you do a tutorial on changing your recovery account or how to create a telegram bot?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
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?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
I updated the post so people could follow this https://github.com/r351574nc3/steem-bot-examples/blob/topic/steemit-account-history/steemit-account-history/src/manage.ts
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
This for utopian, and utopian requires an actual repository instead of a gist, so...
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
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.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Changed.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Ah, ok. Thanks for clarifying that. I'll change it.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Hey @r351574nc3 I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
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
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
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:
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?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
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.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Well, I'm doing nothing special. This is the call:
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?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Oh, that clears things up. Thanks.
It is. Only full nodes contain account history. They are pretty few.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
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?
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit