hugobarauna commited on
Commit
15781db
1 Parent(s): 347b321

Create helius_transaction_render.livemd

Browse files
public-apps/helius_transaction_render.livemd ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- livebook:{"app_settings":{"access_type":"public","output_type":"rich","slug":"helius-transaction-render"}} -->
2
+
3
+ # Helius Transaction Render
4
+
5
+ ```elixir
6
+ Mix.install([
7
+ {:req, "~> 0.3.4"},
8
+ {:jason, "~> 1.4.0"},
9
+ {:kino, "~> 0.12.3"}
10
+ ])
11
+ ```
12
+
13
+ ## Code Setup
14
+
15
+ This section includes all the Elixir code to fetch and render the given transaction
16
+
17
+ You don't need to edit any of it
18
+
19
+ ```elixir
20
+ # Define transaction fetching logic
21
+ defmodule HeliusFetch do
22
+ def fetch_transaction(signature, api_key) do
23
+ transactions_url = "https://api.helius.xyz/v0/transactions"
24
+
25
+ Req.post!(
26
+ transactions_url,
27
+ params: ["api-key": api_key],
28
+ json: %{transactions: [signature]}
29
+ ).body
30
+ |> List.first()
31
+ end
32
+ end
33
+
34
+ Kino.nothing()
35
+ ```
36
+
37
+ ````elixir
38
+ # Define transaction rendering logic
39
+ defmodule TransactionRender do
40
+ defp truncate(string, length) do
41
+ start = String.slice(string, 0, length)
42
+ last = String.slice(string, 0 - length, length)
43
+ start <> "..." <> last
44
+ end
45
+
46
+ defp render_summary(transaction) do
47
+ source = transaction["source"]
48
+ type = transaction["type"]
49
+ description = transaction["description"]
50
+ fee_payer = transaction["feePayer"]
51
+
52
+ Kino.Markdown.new("""
53
+ **Source**: #{source}
54
+
55
+ **Type**: #{type}
56
+
57
+ **Description**: #{description}
58
+
59
+ **Fee Payer**: [#{truncate(fee_payer, 8)}](https://explorer.solana.com/address/#{fee_payer})
60
+ """)
61
+ end
62
+
63
+ defp render_event(name, event) do
64
+ Kino.Markdown.new("""
65
+ ### #{name}
66
+
67
+ ```json
68
+ #{Jason.encode!(event, pretty: true)}
69
+ ```
70
+ """)
71
+ end
72
+
73
+ defp render_events(transaction) do
74
+ events = transaction["events"]
75
+
76
+ case events do
77
+ %{} ->
78
+ Kino.Text.new("No events")
79
+
80
+ _ ->
81
+ events
82
+ |> Enum.map(fn {name, event} -> render_event(name, event) end)
83
+ |> Kino.Layout.grid()
84
+ end
85
+ end
86
+
87
+ defp native_transfer_diagram_line(transfer) do
88
+ from =
89
+ case transfer["fromUserAccount"] do
90
+ "" -> "none"
91
+ address -> truncate(address, 4)
92
+ end
93
+
94
+ to =
95
+ case transfer["toUserAccount"] do
96
+ "" -> "none"
97
+ address -> truncate(address, 4)
98
+ end
99
+
100
+ amount = (transfer["amount"] / 1_000_000_000) |> Float.round(4)
101
+ label = "#{amount} SOL"
102
+ # Mermaid diagram line starting with 2 spaces
103
+ " #{from}-...->|#{label}|#{to}"
104
+ end
105
+
106
+ defp render_native_transfers(transaction) do
107
+ native_transfers = transaction["nativeTransfers"]
108
+
109
+ case native_transfers do
110
+ [] ->
111
+ Kino.Text.new("No native transfers")
112
+
113
+ _ ->
114
+ diagram_lines =
115
+ native_transfers
116
+ |> Enum.filter(fn transfer -> transfer["amount"] > 0 end)
117
+ |> Enum.map(fn transfer -> native_transfer_diagram_line(transfer) end)
118
+ |> Enum.join("\n")
119
+
120
+ diagram = """
121
+ flowchart LR
122
+ #{diagram_lines}
123
+ """
124
+
125
+ Kino.Mermaid.new(diagram)
126
+ end
127
+ end
128
+
129
+ defp token_transfer_diagram_line(transfer) do
130
+ from =
131
+ case transfer["fromUserAccount"] do
132
+ "" -> "none"
133
+ address -> truncate(address, 4)
134
+ end
135
+
136
+ to =
137
+ case transfer["toUserAccount"] do
138
+ "" -> "none"
139
+ address -> truncate(address, 4)
140
+ end
141
+
142
+ token_amount = transfer["tokenAmount"]
143
+ mint = truncate(transfer["mint"], 4)
144
+ label = "#{token_amount} #{mint}"
145
+ " #{from}-...->|#{label}|#{to}"
146
+ end
147
+
148
+ defp render_token_transfers(transaction) do
149
+ token_transfers = transaction["tokenTransfers"]
150
+
151
+ case token_transfers do
152
+ [] ->
153
+ Kino.Text.new("No token transfers")
154
+
155
+ _ ->
156
+ diagram_lines =
157
+ token_transfers
158
+ |> Enum.map(fn transfer -> token_transfer_diagram_line(transfer) end)
159
+ |> Enum.join("\n")
160
+
161
+ diagram = """
162
+ flowchart LR
163
+ #{diagram_lines}
164
+ """
165
+
166
+ Kino.Mermaid.new(diagram)
167
+ end
168
+ end
169
+
170
+ def render(transaction) do
171
+ Kino.Layout.tabs(
172
+ Summary: render_summary(transaction),
173
+ Tree: Kino.Tree.new(transaction),
174
+ Events: render_events(transaction),
175
+ "Native Transfers": render_native_transfers(transaction),
176
+ "Token Transfers": render_token_transfers(transaction)
177
+ )
178
+ end
179
+ end
180
+ ````
181
+
182
+ <!-- livebook:{"branch_parent_index":0} -->
183
+
184
+ ## Fetch a transaction
185
+
186
+ ```elixir
187
+ form =
188
+ Kino.Control.form(
189
+ [
190
+ signature: Kino.Input.text("Transaction Signature"),
191
+ api_key: Kino.Input.password("Helius API Key")
192
+ ],
193
+ submit: "Fetch"
194
+ )
195
+
196
+ form |> Kino.render()
197
+
198
+ frame = Kino.Frame.new()
199
+ ```
200
+
201
+ This next code block does all the magic
202
+
203
+ You just need to evaluate it :)
204
+
205
+ ```elixir
206
+ Kino.listen(form, fn event ->
207
+ signature_length = byte_size(event.data.signature)
208
+ api_key_length = byte_size(event.data.api_key)
209
+
210
+ case {signature_length, api_key_length} do
211
+ {0, _} ->
212
+ Kino.Frame.render(
213
+ frame,
214
+ Kino.Markdown.new("**No transaction signature given**")
215
+ )
216
+
217
+ {_, 0} ->
218
+ Kino.Frame.render(
219
+ frame,
220
+ Kino.Markdown.new("**No Helius API key given**")
221
+ )
222
+
223
+ _ ->
224
+ transaction = HeliusFetch.fetch_transaction(event.data.signature, event.data.api_key)
225
+ Kino.Frame.render(frame, TransactionRender.render(transaction))
226
+ end
227
+ end)
228
+ ```