Published
Edited
Mar 31, 2020
1 star
Insert cell
md`# Ethereum Block Visualiser`
Insert cell
chart = {
const root = treemap(data);

const leaf = svg
.selectAll("g")
.data(root.leaves())
.join("g")
.attr("transform", d => `translate(${d.x0},${d.y0})`);

leaf.append("title").text(
d =>
`${d
.ancestors()
.reverse()
.map(d => d.data.name)
.join("/")}\n${format(d.value)}`
);

leaf
.append("rect")
.attr("id", d => (d.leafUid = DOM.uid("leaf")).id)
.attr("fill", d => {
while (d.depth > 1) d = d.parent;
return color(d.data.name);
})
.attr("fill-opacity", 0.6)
.attr("width", d => d.x1 - d.x0)
.attr("height", d => d.y1 - d.y0);

leaf
.append("clipPath")
.attr("id", d => (d.clipUid = DOM.uid("clip")).id)
.append("use")
.attr("xlink:href", d => d.leafUid.href);

leaf
.append("text")
.attr("clip-path", d => d.clipUid)
.attr("y", "1em")
.text(d => format(d.data.value));

return svg.node();
}
Insert cell
format = d3.format(".3s")
Insert cell
color = d3.scaleOrdinal(d3.schemeCategory10)
Insert cell
treemap(data).leaves()
Insert cell
data = {
var block = {
name: `block ${blockData.number * 1}`,
children: [
//{ name: "calls", children: [] },
{ name: "transfers" },
{
name: "unused",
children: [{ name: "Unused Gas", value: blockData.unusedGas }]
}
]
};
var transfers = await addTransactionsToBlock();

// console.log(transfers);
// var trList = [];
// transfers.forEach(tr => {
// console.log("tr", tr.value);
// trList.push(Object.assign(tr, { value: tr.gasPaid }));
// });
block.children[0].children = transfers;
block.children.unshift({ name: "calls", children: transfers.calls });
block.children.unshift({ name: "transfers", children: transfers.transfers });
return block;
}
Insert cell
addTransactionsToBlock = async () => {
var tfrList = [];
var callList = [];
blockData.transactions.forEach(async hash => {
console.log(hash);
const tx = await getTransaction(hash);
tx.gasPaid = (tx.gas * tx.gasPrice) / 10e+12;
tx.gasPriceGwei = tx.gasPrice / 10e+9;
tx.transferredValue = tx.value / 10e+18;
tx.value = tx.gas * 1;
tx.name = tx.hash;
if (tx.input === '0x') {
tfrList.push(tx);
} else {
callList.push(tx);
}
});
return { calls: callList, transfers: tfrList };
}
Insert cell
blockData = getLatestBlock()
Insert cell
svg = d3
.create("svg")
.attr("viewBox", [0, 0, margin.width, margin.height])
.style("font", "10px sans-serif")
.style("user-select", "none")
.style("background-color", backgroundColour)
.style("color", "#ffffff")
Insert cell
backgroundColour = d3.rgb("#ffffff") //d3.rgb("#023770")
Insert cell
getTransaction = async hash => {
const tx = await rpcCallResult("eth_getTransactionByHash", [hash]);
tx.id = tx.hash;
tx.type = 'transaction';
return tx;
}
Insert cell
getBlock = async blkNum => {
const blkParam =
blkNum === 'latest' ? blkNum : web3.utils.fromDecimal(blkNum);
const blockData = await rpcCallResult("eth_getBlockByNumber", [
blkParam,
false
]);
const unusedGas = blockData.gasLimit - blockData.gasUsed;
blockData.unusedGas = unusedGas;
return blockData;
}
Insert cell
getLatestBlock = async () => {
return getBlock('latest');
}
Insert cell
blockNumber = parseInt(await rpcCallResult("eth_blockNumber"), 16)
Insert cell
rpcCallResult = async (method, params = []) => {
const resp = await rpcCall(method, params);
if (resp.result) {
mutable isConnected = true;
return resp.result;
}
mutable isConnected = false;
return resp.error;
}
Insert cell
rpcCall = async (requestMethod, params) => {
return d3.json(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
method: requestMethod,
params: params,
id: 1
})
});
}
Insert cell
mutable isConnected = false
Insert cell
url = "https://ropsten.infura.io/v3/4eb6b2cd659c40e2a4a565e7a46c1ef6" //"http://localhost:8546"
Insert cell
treemap = data =>
d3
.treemap()
.tile(d3.treemapSquarify)
.size([margin.width, margin.height])
.padding(1)
.round(true)(
d3
.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value)
)
Insert cell
margin = ({
height: 300,
width: 500,
top: 30,
bottom: 10,
left: 20,
right: 20
})
Insert cell
web3 = require("https://cdn.jsdelivr.net/npm/web3@latest/dist/web3.min.js")
Insert cell
d3 = require("d3@5")
Insert cell
md`## License

The code in this notebook is MIT Licensed.

Copyright (c) 2020 Stonebell Consulting Pty Ltd`
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more