Skip to content

Instantly share code, notes, and snippets.

@frol
Created September 25, 2021 19:06
Show Gist options
  • Save frol/307c5058b458ddff48dda483f4463131 to your computer and use it in GitHub Desktop.
Save frol/307c5058b458ddff48dda483f4463131 to your computer and use it in GitHub Desktop.
Live Transactions visualization on NEAR mainnet
// https://youtu.be/2WXaosLThBY
import React, { useState, useEffect } from "react";
import ReactEcharts from "echarts-for-react";
import echarts from "echarts";
import { Tabs, Tab } from "react-bootstrap";
import StatsApi, { AccountsByDate } from "../../libraries/explorer-wamp/stats";
import { Props } from "./TransactionsByDate";
import { Translate } from "react-localize-redux";
const AccountChangesFlow = ({ chartStyle }: Props) => {
const [uniqueAccounts, setUniqueAccounts] = useState(new Map());
const [accountChanges, setAccountChanges] = useState([]);
useEffect(async () => {
const statsApi = new StatsApi();
const result = await statsApi.call('select:INDEXER_BACKEND', [
`select
account_changes.changed_in_block_timestamp as last_changed_in_block_timestamp,
account_changes.index_in_block as last_index_in_block
from account_changes
order by changed_in_block_timestamp desc, index_in_block desc
limit 1`
]
);
console.log("eff3", result)
let { last_changed_in_block_timestamp, last_index_in_block } = result[0];
console.log(last_changed_in_block_timestamp, last_index_in_block)
const pullAccountChanges = async () => {
const newAccountChanges = await statsApi.call('select:INDEXER_BACKEND', [
`select
account_changes.changed_in_block_timestamp,
account_changes.index_in_block,
receipts.predecessor_account_id,
receipts.receiver_account_id
from account_changes
join receipts on account_changes.caused_by_receipt_id = receipts.receipt_id
where
receipts.predecessor_account_id != 'system' and
receipts.predecessor_account_id != receipts.receiver_account_id and
(
changed_in_block_timestamp > :last_changed_in_block_timestamp or
(changed_in_block_timestamp = :last_changed_in_block_timestamp and index_in_block > :last_index_in_block)
)
order by changed_in_block_timestamp desc, index_in_block desc`,
{
last_changed_in_block_timestamp,
last_index_in_block,
}
]);
if (newAccountChanges.length > 0) {
last_changed_in_block_timestamp = newAccountChanges[0].changed_in_block_timestamp;
last_index_in_block = newAccountChanges[0].index_in_block;
}
/*
setUniqueAccounts((state) => {
newAccountChanges.forEach(({ predecessor_account_id, receiver_account_id }) => {
state.add(predecessor_account_id)
state.add(receiver_account_id)
})
return state
})
*/
setAccountChanges((oldAccountChanges) => {
const uniqueAccounts = new Map();
oldAccountChanges.forEach(({ predecessor_account_id, receiver_account_id }) => {
uniqueAccounts.set(predecessor_account_id, (uniqueAccounts.get(predecessor_account_id) || 0) + 1)
uniqueAccounts.set(receiver_account_id, (uniqueAccounts.get(receiver_account_id) || 0) + 1)
})
newAccountChanges.forEach(({ predecessor_account_id, receiver_account_id }) => {
uniqueAccounts.set(predecessor_account_id, (uniqueAccounts.get(predecessor_account_id) || 0) + 1)
uniqueAccounts.set(receiver_account_id, (uniqueAccounts.get(receiver_account_id) || 0) + 1)
})
setUniqueAccounts(uniqueAccounts);
const oldAccountChangesCount = Math.max(0, 300 - newAccountChanges.length);
return [...oldAccountChanges.slice(-oldAccountChangesCount), ...newAccountChanges]
})
};
let timer;
const iterate = async () => {
await pullAccountChanges();
timer = setTimeout(iterate, 1000);
};
timer = setTimeout(iterate, 0);
return () => {
clearTimeout(timer)
}
}, []);
const getOption = () => {
const categories = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
];
console.log(accountChanges)
return {
title: {
text: 'Live NEAR Transactions',
subtext: 'Circular layout',
top: 'bottom',
left: 'right'
},
tooltip: {},
/*legend: [
{
data: graph.categories.map(function (a) {
return a.name;
})
}
],*/
animationDurationUpdate: 1000,
animationEasingUpdate: 'quinticInOut',
series: [
{
name: 'NEAR Transactions',
type: 'graph',
layout: 'circular',
circular: {
rotateLabel: true
},
data: [...uniqueAccounts.entries()].map(([accountId, edges]) => ({ category: edges > 10 ? 9 : edges, name: accountId, symbolSize: edges, label: { show: true } })),
links: accountChanges.map(({ predecessor_account_id: source, receiver_account_id: target }) => ({ source, target })),
categories: categories,
roam: true,
label: {
position: 'right',
formatter: '{b}'
},
lineStyle: {
color: 'source',
curveness: 0.3
}
}
]
};
};
return (
<ReactEcharts
option={getOption()}
style={{ height: 1000 }}
/>
);
};
export default AccountChangesFlow;
@ilyar
Copy link

ilyar commented Sep 25, 2021

👍 JFYI https://gource.io/

@frol
Copy link
Author

frol commented Sep 25, 2021

@ilyar yeah, I used gource as a source of inspiration. Unfortunately, there is nothing I can reuse from gource

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment