livebook / public-apps /helius_transaction_render.livemd
hugobarauna's picture
Update public-apps/helius_transaction_render.livemd
1477b95 verified
<!-- livebook:{"app_settings":{"access_type":"public","output_type":"rich","show_source":true,"slug":"helius-transaction-render"}} -->
# Helius Transaction Render
```elixir
Mix.install([
{:req, "~> 0.3.4"},
{:jason, "~> 1.4.0"},
{:kino, "~> 0.12.3"}
])
```
## Code Setup
This section includes all the Elixir code to fetch and render the given transaction
You don't need to edit any of it
```elixir
# Define transaction fetching logic
defmodule HeliusFetch do
def fetch_transaction(signature, api_key) do
transactions_url = "https://api.helius.xyz/v0/transactions"
response =
Req.post!(
transactions_url,
params: ["api-key": api_key],
json: %{transactions: [signature]}
)
case response do
%Req.Response{status: 200, body: body} ->
{:ok, List.first(body)}
%Req.Response{body: body} ->
{:error, body}
end
end
end
```
```elixir
import Kino.Shorts
```
````elixir
# Define transaction rendering logic
defmodule TransactionRender do
defp truncate(string, length) do
start = String.slice(string, 0, length)
last = String.slice(string, 0 - length, length)
start <> "..." <> last
end
defp render_summary(transaction) do
source = transaction["source"]
type = transaction["type"]
description = transaction["description"]
fee_payer = transaction["feePayer"]
markdown("""
**Source**: #{source}
**Type**: #{type}
**Description**: #{description}
**Fee Payer**: [#{truncate(fee_payer, 8)}](https://explorer.solana.com/address/#{fee_payer})
""")
end
defp render_event(name, event) do
markdown("""
### #{name}
```json
#{Jason.encode!(event, pretty: true)}
```
""")
end
defp render_events(transaction) do
events = transaction["events"]
case events do
%{} ->
text("No events")
_ ->
events
|> Enum.map(fn {name, event} -> render_event(name, event) end)
|> Kino.Layout.grid()
end
end
defp native_transfer_diagram_line(transfer) do
from =
case transfer["fromUserAccount"] do
"" -> "none"
address -> truncate(address, 4)
end
to =
case transfer["toUserAccount"] do
"" -> "none"
address -> truncate(address, 4)
end
amount = (transfer["amount"] / 1_000_000_000) |> Float.round(4)
label = "#{amount} SOL"
# Mermaid diagram line starting with 2 spaces
" #{from}-...->|#{label}|#{to}"
end
defp render_native_transfers(transaction) do
native_transfers = transaction["nativeTransfers"]
case native_transfers do
[] ->
text("No native transfers")
_ ->
diagram_lines =
native_transfers
|> Enum.filter(fn transfer -> transfer["amount"] > 0 end)
|> Enum.map(fn transfer -> native_transfer_diagram_line(transfer) end)
|> Enum.join("\n")
diagram = """
flowchart LR
#{diagram_lines}
"""
mermaid(diagram)
end
end
defp token_transfer_diagram_line(transfer) do
from =
case transfer["fromUserAccount"] do
"" -> "none"
address -> truncate(address, 4)
end
to =
case transfer["toUserAccount"] do
"" -> "none"
address -> truncate(address, 4)
end
token_amount = transfer["tokenAmount"]
mint = truncate(transfer["mint"], 4)
label = "#{token_amount} #{mint}"
" #{from}-...->|#{label}|#{to}"
end
defp render_token_transfers(transaction) do
token_transfers = transaction["tokenTransfers"]
case token_transfers do
[] ->
text("No token transfers")
_ ->
diagram_lines =
token_transfers
|> Enum.map(fn transfer -> token_transfer_diagram_line(transfer) end)
|> Enum.join("\n")
diagram = """
flowchart LR
#{diagram_lines}
"""
mermaid(diagram)
end
end
def render(transaction) do
Kino.Layout.tabs(
Summary: render_summary(transaction),
Tree: tree(transaction),
Events: render_events(transaction),
"Native Transfers": render_native_transfers(transaction),
"Token Transfers": render_token_transfers(transaction)
)
end
end
````
## Fetch a transaction
```elixir
form =
Kino.Control.form(
[
signature:
Kino.Input.text("Transaction Signature",
default:
"5r4xyeKUJkagGvNGpzKd7rE2LuxPJfhZfiub7JrqS28gFHY2Z18H557srUCPbiJQErW2XA4xBoZGQpLjDE8wyFs4"
),
api_key: Kino.Input.password("Helius API Key")
],
submit: "Fetch"
)
form |> Kino.render()
frame = frame()
```
This next code block does all the magic
You just need to evaluate it :)
```elixir
Kino.listen(form, fn event ->
signature_length = byte_size(event.data.signature)
api_key_length = byte_size(event.data.api_key)
origin = event.origin
case {signature_length, api_key_length} do
{0, _} ->
Kino.Frame.render(
frame,
Kino.Markdown.new("**No transaction signature given**"),
to: origin
)
{_, 0} ->
Kino.Frame.render(
frame,
Kino.Markdown.new("**No Helius API key given**"),
to: origin
)
_ ->
case HeliusFetch.fetch_transaction(event.data.signature, event.data.api_key) do
{:ok, transaction} ->
Kino.Frame.render(frame, TransactionRender.render(transaction), to: origin)
{:error, error} ->
Kino.Frame.render(frame, markdown("*Error*: #{inspect(error)}"), to: origin)
end
end
end)
```