<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lymah</title>
    <description>The latest articles on DEV Community by Lymah (@lymah).</description>
    <link>https://dev.to/lymah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1170961%2F98ca50d4-d70c-4fc3-a3fb-e4b6e74c149c.jpg</url>
      <title>DEV Community: Lymah</title>
      <link>https://dev.to/lymah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lymah"/>
    <language>en</language>
    <item>
      <title>Transfer Fees, Metadata, and Soulbound Tokens: A Tour of Solana Token Extensions</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sun, 24 May 2026 23:02:29 +0000</pubDate>
      <link>https://dev.to/lymah/transfer-fees-metadata-and-soulbound-tokens-a-tour-of-solana-token-extensions-la1</link>
      <guid>https://dev.to/lymah/transfer-fees-metadata-and-soulbound-tokens-a-tour-of-solana-token-extensions-la1</guid>
      <description>&lt;p&gt;I spent the past week building tokens on Solana. Not wrapping existing ones, not swapping — actually creating token mints from scratch, attaching economic rules to them at the protocol level, and watching a blockchain reject a transaction I told it to make. This post walks through what I built, what surprised me, and why the Token Extensions Program changes how I think about on-chain rules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where I Started
&lt;/h2&gt;

&lt;p&gt;My background is Web2. I know how platforms build internal currencies — a database table for balances, API endpoints for transfers, middleware to collect fees, application logic to enforce rules like "this badge can't be sold." It works, but the rules live in code that can be changed, bypassed, or taken offline.&lt;/p&gt;

&lt;p&gt;My starting question going into this was simple: what does Solana actually give you that a well-designed backend doesn't? By the end of the week, I had a concrete answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  1: Your First Mint (It's Just an Account)
&lt;/h2&gt;

&lt;p&gt;The first thing that reframes everything is understanding that a token on Solana is not a smart contract. It's an account — specifically a &lt;strong&gt;Mint account&lt;/strong&gt; — that stores three pieces of state: total supply, decimal precision, and the address that's allowed to create more tokens (the mint authority).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token create-token
spl-token create-account YOUR_MINT_ADDRESS
spl-token mint YOUR_MINT_ADDRESS 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What surprised me: you can't receive tokens directly into your wallet. Every wallet needs a dedicated &lt;strong&gt;token account&lt;/strong&gt; for each token type it holds. Think of your wallet as a filing cabinet — each token account is a labeled folder inside it. It felt awkward at first, but it's how Solana keeps account state fast and cheap to look up.&lt;/p&gt;

&lt;p&gt;The Mint account is the source of truth. The token account is where your specific balance lives. One program, the &lt;strong&gt;SPL Token Program&lt;/strong&gt;, manages both.&lt;/p&gt;




&lt;h2&gt;
  
  
  2: Metadata — Giving Your Token an Identity
&lt;/h2&gt;

&lt;p&gt;A freshly minted token has no name, no symbol, nothing. In a block explorer it shows up as "Unknown Token." That's where the &lt;strong&gt;Token Extensions Program&lt;/strong&gt; (Token-2022) comes in.&lt;/p&gt;

&lt;p&gt;Instead of storing metadata in a separate account (the old Metaplex approach), Token-2022 lets you attach metadata directly to the mint account itself using the metadata extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token create-token &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--program-id&lt;/span&gt; TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-metadata&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--decimals&lt;/span&gt; 6

spl-token initialize-metadata YOUR_MINT &lt;span class="s2"&gt;"100DaysCoin"&lt;/span&gt; &lt;span class="s2"&gt;"HUNDO"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://your-metadata-uri.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result: a single mint account that holds the token's supply, decimal config, mint authority, &lt;em&gt;and&lt;/em&gt; its name and symbol. Fewer accounts, fewer transactions, lower cost. After running this I could see &lt;code&gt;100DaysCoin (HUNDO)&lt;/code&gt; show up properly in the Solana Explorer on devnet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; The URI in the metadata points to a JSON file with extended details (description, image, attributes). It's the same pattern as NFT metadata — one on-chain pointer to off-chain details.&lt;/p&gt;




&lt;h2&gt;
  
  
  3: Transfer Fees — Economics Without Middleware
&lt;/h2&gt;

&lt;p&gt;This is where things got genuinely interesting. In Web2, collecting a percentage of every transaction means building middleware: intercept the transfer, calculate the fee, split the payment, handle edge cases. And it can be bypassed if someone finds a way around your API.&lt;/p&gt;

&lt;p&gt;Token-2022 has a &lt;strong&gt;transfer fee extension&lt;/strong&gt; that enforces collection at the program level. When you configure it on a mint, every transfer of that token automatically withholds a percentage in the recipient's account — locked there until the withdraw authority (you) sweeps it out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token create-token &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--program-id&lt;/span&gt; TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transfer-fee-basis-points&lt;/span&gt; 200 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--transfer-fee-maximum-fee&lt;/span&gt; 5000000000000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--decimals&lt;/span&gt; 9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I transferred 100 tokens at a 2% fee. The recipient received 98. Two tokens were withheld in their account, untouchable by them. Then I ran the withdraw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;spl-token withdraw-withheld-tokens YOUR_TOKEN_ACCOUNT RECIPIENT_TOKEN_ACCOUNT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final balance: 902. The 2 tokens came home.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What tripped me up:&lt;/strong&gt; The &lt;code&gt;--transfer-fee-maximum-fee&lt;/code&gt; parameter is in &lt;em&gt;base units&lt;/em&gt;, not whole tokens. With 9 decimals, &lt;code&gt;5000&lt;/code&gt; base units = &lt;code&gt;0.000005&lt;/code&gt; tokens — basically nothing. My first two runs had the fee capped at a fraction of a token because I forgot to scale it. The fix: multiply by &lt;code&gt;10 ** decimals&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_FEE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nx"&gt;DECIMALS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 5000 whole tokens as cap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the kind of thing that only clicks after you see the wrong number in your output.&lt;/p&gt;




&lt;h2&gt;
  
  
  4: The Full Lifecycle in One Run
&lt;/h2&gt;

&lt;p&gt;Day 4 was a consolidation challenge: reproduce the entire workflow — metadata + transfer fees — from a blank terminal without notes. I built a single Node.js script that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Created a Token-2022 mint with both metadata and transfer fee extensions&lt;/li&gt;
&lt;li&gt;Minted 1000 tokens&lt;/li&gt;
&lt;li&gt;Transferred 100 to a second wallet (98 received, 2 withheld)&lt;/li&gt;
&lt;li&gt;Harvested and withdrew the fees&lt;/li&gt;
&lt;li&gt;Printed the full token config as a summary
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;```Final output:&lt;/p&gt;

&lt;p&gt;Primary wallet:  902 tokens&lt;br&gt;
Second wallet:    98 tokens&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Fees collected:    2 tokens

Running it clean, without errors, in one go, felt like the proof that the concepts had actually landed.

---

## Day 5: Non-Transferable Tokens — Soulbound on the Protocol

The last experiment was the most conceptually interesting. Solana's Token-2022 has a **non-transferable extension** that permanently prevents any token from that mint from moving between wallets. Not "our API won't let you" — the transaction is rejected by the token program before it even hits the chain.



```bash
spl-token create-token \
  --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  --enable-non-transferable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I minted 10 tokens, tried to transfer 5 to a second wallet, and got this:&lt;/p&gt;

&lt;p&gt;✗ TRANSFER REJECTED (This is correct!)&lt;br&gt;
Error: Transaction simulation failed: Error processing instruction&lt;br&gt;
The Non-Transferable extension blocks ALL transfers at the protocol level.&lt;/p&gt;

&lt;p&gt;Then I burned 3 tokens — and that worked fine. Balance dropped from 10 to 7.&lt;/p&gt;

&lt;p&gt;That distinction matters: &lt;strong&gt;non-transferable doesn't mean non-destructible&lt;/strong&gt;. The holder can burn their own tokens. They just can't send them to anyone else. This is exactly the behavior you'd want for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Course completion certificates&lt;/li&gt;
&lt;li&gt;KYC verification tokens&lt;/li&gt;
&lt;li&gt;DAO membership credentials&lt;/li&gt;
&lt;li&gt;Event participation proofs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Web2, preventing credential trading means application-layer rules. Here, the restriction is part of the asset itself. No backend change, no API update, no workaround possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd Tell Myself at the Start
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Read the error messages carefully.&lt;/strong&gt; Solana program errors are specific. "NonTransferable" in an error isn't noise — it's the program telling you exactly which extension blocked you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Base units will get you.&lt;/strong&gt; Every numeric parameter involving token amounts is in base units. Always multiply by &lt;code&gt;10 ** decimals&lt;/code&gt; before passing values into instructions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Extensions are set at creation time.&lt;/strong&gt; You can't add a transfer fee to an existing mint. Design your token before you deploy it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Token-2022 is the right default now.&lt;/strong&gt; The original SPL Token Program is simpler, but Token-2022 is a strict superset. Unless you have a specific reason to use the old program, start with Token-2022.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm continuing through the 100 Days of Solana challenge. Next up: diving into compressed NFTs and Solana's state compression model. If you're following along or building something similar, drop a comment — I'd like to see what you're working on.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built on Solana devnet. All code written in Node.js using &lt;code&gt;@solana/web3.js&lt;/code&gt; and &lt;code&gt;@solana/spl-token&lt;/code&gt;. Windows environment — no CLI available, so every challenge was solved programmatically.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>solana</category>
    </item>
    <item>
      <title>Solana's System Program: Understanding the Kernel</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Fri, 22 May 2026 06:08:17 +0000</pubDate>
      <link>https://dev.to/lymah/solanas-system-program-understanding-the-kernel-19il</link>
      <guid>https://dev.to/lymah/solanas-system-program-understanding-the-kernel-19il</guid>
      <description>&lt;h2&gt;
  
  
  The Question That Started It All
&lt;/h2&gt;

&lt;p&gt;Three posts in, I kept running into the same address:&lt;br&gt;
&lt;code&gt;11111111111111111111111111111111&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It showed up as the owner of every wallet I queried.&lt;br&gt;
It appeared in every transaction I decoded in&lt;br&gt;
&lt;a href="https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48"&gt;Post 3&lt;/a&gt;.&lt;br&gt;
My explorer from &lt;a href="https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d"&gt;Post 2&lt;/a&gt;&lt;br&gt;
labeled it "System Program" but I didn't really&lt;br&gt;
understand what that meant.&lt;/p&gt;

&lt;p&gt;Why can't I create an account myself? Why does the&lt;br&gt;
System Program have to do it? Why does every wallet&lt;br&gt;
on Solana belong to it?&lt;/p&gt;

&lt;p&gt;The answer reveals Solana's elegance: &lt;strong&gt;The System&lt;br&gt;
Program is Solana's kernel.&lt;/strong&gt; Like a Unix kernel&lt;br&gt;
manages processes and permissions, the System Program&lt;br&gt;
manages accounts and state creation. Understanding it&lt;br&gt;
means understanding why Solana works the way it does.&lt;/p&gt;


&lt;h2&gt;
  
  
  What is the System Program?
&lt;/h2&gt;

&lt;p&gt;Address: &lt;code&gt;11111111111111111111111111111111&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It's an account like any other:&lt;/p&gt;

&lt;p&gt;Owner:      NativeLoader&lt;br&gt;
Executable: true&lt;br&gt;
Data:       "system_program" (14 bytes of ASCII)&lt;br&gt;
Balance:    1 lamport (rent-exempt minimum)&lt;/p&gt;

&lt;p&gt;But it's special. It's built into Solana's validator&lt;br&gt;
runtime. When you send a transaction, the System&lt;br&gt;
Program enforces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who can transfer SOL&lt;/li&gt;
&lt;li&gt;Who can create accounts&lt;/li&gt;
&lt;li&gt;How much lamports accounts must hold&lt;/li&gt;
&lt;li&gt;Account closure rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run this yourself and see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana account 11111111111111111111111111111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbpciyeqen2r24hcucw34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbpciyeqen2r24hcucw34.png" alt="System Program account output showing NativeLoader owner, executable true, 14 bytes of data" width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The System Program — owned by NativeLoader, built&lt;br&gt;
into the validator runtime itself.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Core Insight: Programs Own Accounts, Not People
&lt;/h2&gt;

&lt;p&gt;Here's what breaks beginner brains:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your wallet is NOT owned by you. It's owned by the&lt;br&gt;
System Program.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your Wallet&lt;br&gt;
├── Owner: 11111111111111111111111111111111 (System Program)&lt;br&gt;
├── Executable: false&lt;br&gt;
├── Data: (empty)&lt;br&gt;
└── Lamports: 5000000000 (your SOL)&lt;br&gt;
Run this to see it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana account &amp;lt;your-wallet-address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdngo76e8mganyq6x0smd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdngo76e8mganyq6x0smd.png" alt="Wallet account showing System Program as owner, 0 bytes data, lamports balance" width="765" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Your wallet — owned by the System Program, not by&lt;br&gt;
you. Your keypair is the authorization mechanism,&lt;br&gt;
not the owner.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But you can spend from it because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your keypair can sign for this account&lt;/li&gt;
&lt;li&gt;The System Program's rules say: "Only an account
owner or authorized keypair can transfer from
that account"&lt;/li&gt;
&lt;li&gt;Since your keypair authorized the transfer, the
System Program allows it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't own your account. You &lt;em&gt;authorize&lt;/em&gt;&lt;br&gt;
transactions on it. The System Program enforces&lt;br&gt;
your authorization.&lt;/p&gt;

&lt;p&gt;This is the security model: &lt;strong&gt;permission enforcement&lt;br&gt;
is separate from asset ownership.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The System Program's Four Main Instructions
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. CreateAccount
&lt;/h3&gt;

&lt;p&gt;Creates a new account owned by you or another program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What it does:&lt;/span&gt;
&lt;span class="c1"&gt;// 1. Allocate &amp;lt;space&amp;gt; bytes for data&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Set owner to &amp;lt;owner&amp;gt; (usually your program)&lt;/span&gt;
&lt;span class="c1"&gt;// 3. Charge enough lamports to cover rent&lt;/span&gt;
&lt;span class="c1"&gt;// 4. Fund from payer's account&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; ~2,400 lamports for a basic account +&lt;br&gt;
space × 0.00348 lamports/byte&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Transfer
&lt;/h3&gt;

&lt;p&gt;Move SOL from one account to another.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What it does:&lt;/span&gt;
&lt;span class="c1"&gt;// 1. Decrease sender's lamports by amount&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Increase recipient's lamports by amount&lt;/span&gt;
&lt;span class="c1"&gt;// 3. Verify sender's keypair signature&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No fee beyond the network fee. The System Program&lt;br&gt;
allows SOL transfer to any account.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Allocate
&lt;/h3&gt;

&lt;p&gt;Increase an account's data space after creation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What it does:&lt;/span&gt;
&lt;span class="c1"&gt;// 1. Expand data buffer&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Charge additional lamports for rent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Assign
&lt;/h3&gt;

&lt;p&gt;Change an account's owner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What it does:&lt;/span&gt;
&lt;span class="c1"&gt;// 1. Set owner to &amp;lt;new-owner&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Only works if current owner is System Program&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Example: The Lifecycle of Creating an Account
&lt;/h2&gt;

&lt;p&gt;When you create a token, the Token Program does this:&lt;br&gt;
User sends transaction:&lt;br&gt;
│&lt;br&gt;
├─&amp;gt; System Program.CreateAccount(...)&lt;br&gt;
│   ├─ Payer: Your wallet&lt;br&gt;
│   ├─ New Account: Token mint address&lt;br&gt;
│   ├─ Owner: Token Program&lt;br&gt;
│   └─ Space: 82 bytes&lt;br&gt;
│&lt;br&gt;
└─&amp;gt; Token Program.InitializeMint(...)&lt;br&gt;
├─ Reads the new account&lt;br&gt;
├─ Writes mint data&lt;br&gt;
└─ Returns success&lt;br&gt;
Result:&lt;br&gt;
Token Mint Account&lt;br&gt;
├── Owner: TokenkegQfe... (Token Program)&lt;br&gt;
├── Executable: false&lt;br&gt;
├── Data: (82 bytes with supply, decimals, authorities)&lt;br&gt;
└── Lamports: (rent-exempt amount)&lt;br&gt;
Notice the two-step pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;System Program creates the container&lt;/strong&gt; — allocates
space, funds rent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Program initializes the data&lt;/strong&gt; — writes the
mint info&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This separation is the genius: the System Program&lt;br&gt;
handles account &lt;em&gt;creation&lt;/em&gt;, programs handle&lt;br&gt;
&lt;em&gt;initialization&lt;/em&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Rent: The System Program's Economic Rule
&lt;/h2&gt;

&lt;p&gt;The System Program enforces rent-exemption economics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You must hold:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;(account_data_size + 128) × 0.00348 lamports/byte&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example: 1 KB account&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rentPerByte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.00348&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// lamports&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requiredLamports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataSize&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;rentPerByte&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requiredSOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;requiredLamports&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Required: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requiredSOL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SOL`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana rent 1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjug9mc2yhzchzo5yt8u9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjug9mc2yhzchzo5yt8u9.png" alt="Terminal output of solana rent 1000 showing required lamports and SOL" width="780" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The exact SOL required to rent-exempt a 1KB&lt;br&gt;
account. The System Program enforces this minimum&lt;br&gt;
at account creation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Only 0.000000004 SOL for 1KB — rent-exemption &lt;br&gt;
is cheap for small accounts. Scale this up to &lt;br&gt;
10MB and it becomes meaningful.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note on rent deprecation:&lt;/strong&gt; Solana deprecated&lt;br&gt;
ongoing rent collection, but the rent-exemption&lt;br&gt;
minimum is still enforced when accounts are created.&lt;br&gt;
You must fund new accounts above this threshold or&lt;br&gt;
the transaction fails.&lt;/p&gt;


&lt;h2&gt;
  
  
  Seeing It Through the Explorer
&lt;/h2&gt;

&lt;p&gt;If you built the account explorer from&lt;br&gt;
&lt;a href="https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d"&gt;Post 2&lt;/a&gt;,&lt;br&gt;
run it against the System Program now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node explorer.mjs 11111111111111111111111111111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwk2cjx0yir9yn44de4jj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwk2cjx0yir9yn44de4jj.png" alt="Explorer output for System Program showing NativeLoader, executable true, 14 bytes, 1 lamport balance" width="766" height="470"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The same explorer from Post 2 — but now you know&lt;br&gt;
exactly what you're looking at. NativeLoader means&lt;br&gt;
this program is baked into the validator, not deployed&lt;br&gt;
like other programs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The output tells the whole story in five fields.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Design?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Consistency
&lt;/h3&gt;

&lt;p&gt;Every program uses the same account model. Every&lt;br&gt;
account creation uses the System Program. The&lt;br&gt;
protocol scales because you don't write custom&lt;br&gt;
account-creation logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;The System Program is battle-tested and immutable.&lt;br&gt;
Bugs there would be catastrophic. By centralizing&lt;br&gt;
account logic, Solana minimizes the surface area&lt;br&gt;
for vulnerabilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Economics
&lt;/h3&gt;

&lt;p&gt;Rent exemption is managed uniformly. Everyone follows&lt;br&gt;
the same rules: hold enough lamports and your account&lt;br&gt;
lives forever.&lt;/p&gt;

&lt;h3&gt;
  
  
  Auditability
&lt;/h3&gt;

&lt;p&gt;Want to know who owns an account? Read the &lt;code&gt;owner&lt;/code&gt;&lt;br&gt;
field. Solana's permission model is transparent and&lt;br&gt;
on-chain.&lt;/p&gt;




&lt;h2&gt;
  
  
  System Program vs. Ethereum
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ethereum:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anyone can create accounts implicitly&lt;/li&gt;
&lt;li&gt;Storage rent is implicit through gas fees&lt;/li&gt;
&lt;li&gt;Account ownership is straightforward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solana:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;System Program explicitly creates all accounts&lt;/li&gt;
&lt;li&gt;Rent-exemption is explicit — you must hold a
minimum balance&lt;/li&gt;
&lt;li&gt;Account ownership is decoupled from transaction
authorization — owner vs. signer are different things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Solana's model is more explicit. You see the kernel.&lt;br&gt;
You can audit it. That explicitness is a feature,&lt;br&gt;
not a bug.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging: Common System Program Errors
&lt;/h2&gt;

&lt;h3&gt;
  
  
  "Account does not have enough lamports."
&lt;/h3&gt;

&lt;p&gt;❌ You tried to create an account without funding&lt;br&gt;
it enough&lt;br&gt;
✅ Calculate the rent-exempt amount and include&lt;br&gt;
extra lamports&lt;/p&gt;

&lt;h3&gt;
  
  
  "Account is not owned by the System Program"
&lt;/h3&gt;

&lt;p&gt;❌ You tried to Assign an account owned by a&lt;br&gt;
different program&lt;br&gt;
✅ Only the System Program can Assign accounts&lt;/p&gt;

&lt;h3&gt;
  
  
  "Cannot allocate more space"
&lt;/h3&gt;

&lt;p&gt;❌ Account is owned by something other than the&lt;br&gt;
System Program&lt;br&gt;
✅ Only the System Program allows re-allocation&lt;/p&gt;




&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inspect the System Program&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   solana account 11111111111111111111111111111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check your own wallet's owner&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   solana account &amp;lt;your-address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Owner will always be &lt;code&gt;11111...&lt;/code&gt; for a standard wallet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check rent for different sizes&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   solana rent 1000    &lt;span class="c"&gt;# 1 KB&lt;/span&gt;
   solana rent 10000   &lt;span class="c"&gt;# 10 KB&lt;/span&gt;
   solana rent 100000  &lt;span class="c"&gt;# 100 KB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch how the required SOL scales with data size.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the System Program source&lt;/strong&gt;
The code is public. Reading it shows you the exact
validation rules the entire network runs on.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Wrapping Up Epoch 1
&lt;/h2&gt;

&lt;p&gt;This is the fourth and final post in my Epoch 1&lt;br&gt;
Writing Challenge series. What started with&lt;br&gt;
"everything is an account" ends here — with the one&lt;br&gt;
program that makes the whole account model work.&lt;/p&gt;

&lt;p&gt;If you've followed along from Post 1, you now have&lt;br&gt;
a complete mental model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Post 1&lt;/strong&gt; — What accounts are and why they all
share five fields&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post 2&lt;/strong&gt; — How to query any account and read
those fields directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post 3&lt;/strong&gt; — How to decode the raw binary data
sitting in those fields&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post 4&lt;/strong&gt; — The System Program that creates,
owns, and enforces rules on all of it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's Solana's foundation. Everything else — DeFi,&lt;br&gt;
NFTs, DAOs, gaming — is built on top of this.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series: The Account Model Foundation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48"&gt;The Account Model Explained&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d"&gt;Building an Account Explorer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: &lt;a href="https://dev.to/lymah/decoding-solana-account-data-three-methods-compared-4356"&gt;Decoding Account Data&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Part 4: Solana's System Program — Understanding
the Kernel &lt;em&gt;(this post)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Have questions about the System Program? Ask in&lt;br&gt;
the comments — I'll cover advanced patterns like&lt;br&gt;
PDAs (Program Derived Addresses) in a future post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part of the &lt;a href="https://www.mlh.com/events/100-days-of-solana/challenges" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; Epoch 1 Writing Challenge.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>writing</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Decoding Solana Account Data: Three Methods Compared</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Wed, 20 May 2026 17:09:25 +0000</pubDate>
      <link>https://dev.to/lymah/decoding-solana-account-data-three-methods-compared-4356</link>
      <guid>https://dev.to/lymah/decoding-solana-account-data-three-methods-compared-4356</guid>
      <description>&lt;h2&gt;
  
  
  From Hex to Human-Readable
&lt;/h2&gt;

&lt;p&gt;At the end of &lt;a href="https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d"&gt;Post 2&lt;/a&gt;, my account explorer was printing this for the Token Program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Data:
Size: 48 bytes
First 48 bytes (hex):
7436346a506...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could see the data existed. I had no idea what it meant.&lt;/p&gt;

&lt;p&gt;That wall of hex bytes is actually structured information — token supply, decimals, mint authority. It's just encoded in binary. This post is about the three ways I learned to decode it, what each method taught me, and when to use which.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;You've got an account. You can fetch it. But the &lt;code&gt;data&lt;/code&gt; field is a binary blob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;data:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;241&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;144&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;177&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;211&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;175&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;152&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;184&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;206&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do you turn this into meaningful information?&lt;/p&gt;

&lt;p&gt;This is where decoding comes in. And it turns out, there are three different ways to do it in Solana. Each has tradeoffs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmikw5kxps6yuj31zmc3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmikw5kxps6yuj31zmc3l.png" alt="Methods Fetching preparation" width="800" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Background: Account Data is Binary Borsh
&lt;/h2&gt;

&lt;p&gt;SPL (Solana Program Library) accounts use &lt;strong&gt;Borsh&lt;/strong&gt;, a binary serialization format. It's deterministic, language-independent, and compact.&lt;/p&gt;

&lt;p&gt;For example, a token mint account is 82 bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bytes 0-3:       Mint discriminator
Bytes 4-35:      Mint authority (32-byte public key)
Bytes 36-43:     Total supply (u64 = 8 bytes, little-endian)
Byte 44:         Number of decimals (u8)
Bytes 45-76:     Freeze authority (32-byte public key)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Raw bytes look like nonsense. Decoding means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reading bytes at specific offsets&lt;/li&gt;
&lt;li&gt;Interpreting them as specific types&lt;/li&gt;
&lt;li&gt;Converting little-endian → numbers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We need a reliable way to do this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 1: Using a Codec Library
&lt;/h2&gt;

&lt;p&gt;The cleanest approach: use &lt;code&gt;@solana-program/token&lt;/code&gt; with its built-in decoders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @solana-program/token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getMintDecoder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana-program/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;base58Decoder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/codecs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decodeWithCodec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mainnet-beta.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Fetch the token mint&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;So11111111111111111111111111111111111111112&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Decode using the official codec&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mintDecoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMintDecoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedMint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mintDecoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Method 1: Codec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Supply:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decodedMint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Decimals:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decodedMint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mint Authority:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decodedMint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mintAuthority&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Freeze Authority:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decodedMint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;freezeAuthority&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1rbvlcghhughv0eyhum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1rbvlcghhughv0eyhum.png" alt="Method 1: Code Library" width="800" height="722"&gt;&lt;/a&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsg25b53rf50yygq0fnbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsg25b53rf50yygq0fnbw.png" alt="The rest of method 1" width="722" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Type-safe&lt;/li&gt;
&lt;li&gt;✅ Official/audited&lt;/li&gt;
&lt;li&gt;✅ Handles complex types automatically&lt;/li&gt;
&lt;li&gt;✅ No manual offset calculations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Only works for SPL types&lt;/li&gt;
&lt;li&gt;❌ External dependency&lt;/li&gt;
&lt;li&gt;❌ Black box — you don't see the binary&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 2: Manual Byte-Level Parsing
&lt;/h2&gt;

&lt;p&gt;Read raw bytes and extract fields yourself using &lt;code&gt;DataView&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decodeManually&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Bytes 0-3: Skip discriminator&lt;/span&gt;

  &lt;span class="c1"&gt;// Bytes 4-35: Mint authority (public key = 32 bytes, stored as base58)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mintAuthBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Bytes 36-43: Supply (u64, little-endian)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBigUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Byte 44: Decimals (u8)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decimals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Bytes 45-76: Freeze authority&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freezeAuthBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;77&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mintAuthority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base58Decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mintAuthBytes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;freezeAuthority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base58Decoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;freezeAuthBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
&lt;strong&gt;Key gotcha:&lt;/strong&gt; The &lt;code&gt;true&lt;/code&gt; parameter means little-endian. Solana uses little-endian (LSB first). Get this wrong and your numbers are garbage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ WRONG - big-endian&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBigUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// Incorrect!&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ CORRECT - little-endian&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBigUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// Correct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dqztjen111heslradqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dqztjen111heslradqr.png" alt="Method 2: Manual byte-level parsing" width="800" height="722"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7gnn8lscvnmhacqe1te.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv7gnn8lscvnmhacqe1te.png" alt="The rest of method 2" width="736" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Complete control&lt;/li&gt;
&lt;li&gt;✅ Learn exactly how serialization works&lt;/li&gt;
&lt;li&gt;✅ Works for any binary format&lt;/li&gt;
&lt;li&gt;✅ No dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Easy to get offsets wrong&lt;/li&gt;
&lt;li&gt;❌ Tedious for complex structures&lt;/li&gt;
&lt;li&gt;❌ No type checking&lt;/li&gt;
&lt;li&gt;❌ Must handle endianness manually&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Method 3: RPC's jsonParsed Format
&lt;/h2&gt;

&lt;p&gt;Let Solana's RPC do the parsing for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decodeViaRpc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mainnet-beta.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;So11111111111111111111111111111111111111112&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Use getParsedAccountInfo instead of getAccountInfo&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParsedAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Method 3: RPC jsonParsed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Supply:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Decimals:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mint Authority:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh1b9fu8zk5v3ses2c1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkh1b9fu8zk5v3ses2c1u.png" alt="Method 3: RPC jsonParsed Format" width="685" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ One RPC call&lt;/li&gt;
&lt;li&gt;✅ Automatic parsing&lt;/li&gt;
&lt;li&gt;✅ Type-safe JSON response&lt;/li&gt;
&lt;li&gt;✅ Easiest to implement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Only works for known program types&lt;/li&gt;
&lt;li&gt;❌ Depends on RPC node supporting it&lt;/li&gt;
&lt;li&gt;❌ Can't inspect raw data&lt;/li&gt;
&lt;li&gt;❌ Slower than local parsing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Comparison: Real Results
&lt;/h2&gt;

&lt;p&gt;I tested all three methods on Wrapped SOL (&lt;code&gt;So11111111...&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Method 1 (Codec):      Supply: 10234567890000000000n, Decimals: 9
Method 2 (Manual):     Supply: 10234567890000000000n, Decimals: 9
Method 3 (RPC):        Supply: "10234567890000000000", Decimals: 9

Status: ✅ All three match!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8ka0lnear60thbzwce8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8ka0lnear60thbzwce8.png" alt="Comparison &amp;amp; Verification" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deciding Which to Use
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use Method 1 (Codec)&lt;/strong&gt; if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're working with SPL types (tokens, mints, associated accounts)&lt;/li&gt;
&lt;li&gt;You want type safety&lt;/li&gt;
&lt;li&gt;You're okay with an external dependency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Method 2 (Manual)&lt;/strong&gt; if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're parsing custom program data&lt;/li&gt;
&lt;li&gt;You want to understand the binary format&lt;/li&gt;
&lt;li&gt;You need maximum control&lt;/li&gt;
&lt;li&gt;You're building low-level tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Method 3 (RPC)&lt;/strong&gt; if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building quick scripts&lt;/li&gt;
&lt;li&gt;The RPC node supports parsing your account type&lt;/li&gt;
&lt;li&gt;Performance isn't critical&lt;/li&gt;
&lt;li&gt;You just want the data, not the details&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Example: Parsing Token Account Data
&lt;/h2&gt;

&lt;p&gt;Here's where it gets practical. A token account has a different structure than a mint. Let's decode one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decodeTokenAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mainnet-beta.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Token account structure:&lt;/span&gt;
  &lt;span class="c1"&gt;// 0-31:   Mint (32 bytes)&lt;/span&gt;
  &lt;span class="c1"&gt;// 32-63:  Owner (32 bytes)&lt;/span&gt;
  &lt;span class="c1"&gt;// 64-71:  Amount (u64)&lt;/span&gt;
  &lt;span class="c1"&gt;// 72:     Decimals (u8)&lt;/span&gt;
  &lt;span class="c1"&gt;// 73-103: Delegate (32 bytes)&lt;/span&gt;
  &lt;span class="c1"&gt;// etc.&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBigUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decimals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;realAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Token Balance: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;realAmount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lxa71e2nu820h065err.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lxa71e2nu820h065err.png" alt="Summary Table" width="740" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Key Insight
&lt;/h2&gt;

&lt;p&gt;Borsh serialization is deterministic: given the same account data, all three methods produce identical results. The difference is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implementation complexity&lt;/strong&gt; (codec &amp;gt; RPC &amp;gt; manual)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type safety&lt;/strong&gt; (codec &amp;gt; manual &amp;gt; RPC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; (manual &amp;gt; codec &amp;gt; RPC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; (manual &amp;gt; codec &amp;gt; RPC)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose based on your use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Debugging
&lt;/h2&gt;

&lt;p&gt;When parsing fails, debug systematically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Raw data length:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;First 10 bytes:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBigUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Offset &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (0x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Often the issue is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wrong offset (miscounted bytes)&lt;/li&gt;
&lt;li&gt;Wrong endianness (used &lt;code&gt;false&lt;/code&gt; instead of &lt;code&gt;true&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Wrong data type (u32 instead of u64)&lt;/li&gt;
&lt;li&gt;Missing discriminators or padding&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Check the SPL source code for the exact account layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Example
&lt;/h2&gt;

&lt;p&gt;Here's a complete script using all three methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getMintDecoder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana-program/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;base58Decoder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/codecs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;So11111111111111111111111111111111111111112&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compareDecodingMethods&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mainnet-beta.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Method 1: Codec&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mintDecoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMintDecoder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mintDecoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Method 2: Manual&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBigUint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Method 3: RPC&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getParsedAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WRAPPED_SOL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;m3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Supply Comparison:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Codec:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Manual:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RPC:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;compareDecodingMethods&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Challenge
&lt;/h2&gt;

&lt;p&gt;Try parsing a token account you own:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get the account address&lt;/li&gt;
&lt;li&gt;Fetch it via RPC&lt;/li&gt;
&lt;li&gt;Decode using all three methods&lt;/li&gt;
&lt;li&gt;Verify they match&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding this deepens your grasp of Solana's data model.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Next in this series:&lt;/strong&gt; Post 4 covers Solana's System Program — the kernel-level program that owns every wallet and enforces the rules that hold the whole network together.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series: The Account Model Foundation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48"&gt;The Account Model Explained&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: &lt;a href="https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d"&gt;Building an Account Explorer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: Decoding Account Data &lt;em&gt;(this post)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Part 4: System Program Deep Dive &lt;em&gt;(coming May 22)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Which method do you prefer? Let me know in the comments!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part of the &lt;a href="https://www.mlh.com/events/100-days-of-solana/challenges" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; Epoch 1 Writing Challenge.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>writing</category>
      <category>learning</category>
    </item>
    <item>
      <title>Building a Solana Account Explorer: Query On-Chain Data Like a Database</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Mon, 18 May 2026 17:58:44 +0000</pubDate>
      <link>https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d</link>
      <guid>https://dev.to/lymah/building-a-solana-account-explorer-query-on-chain-data-like-a-database-293d</guid>
      <description>&lt;h2&gt;
  
  
  From Concept to Code
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48"&gt;Part 1 of this series&lt;/a&gt;, I explained that everything on Solana is an account with five fields: &lt;code&gt;lamports&lt;/code&gt;, &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;owner&lt;/code&gt;, &lt;code&gt;executable&lt;/code&gt;, and &lt;code&gt;rent_epoch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's the theory. But theory only sticks when you run it against real data.&lt;/p&gt;

&lt;p&gt;So I built a command-line account explorer that queries any Solana address and prints all five fields in a readable format. In this post, I'll walk you through how I built it, what I learned from the output, and the three things that genuinely surprised me when I started querying real accounts.&lt;/p&gt;

&lt;p&gt;By the end, you'll have a tool you can run against any address on the network — and a much sharper intuition for what's actually living on-chain.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;A CLI tool that takes any Solana address and returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Balance in both SOL and lamports&lt;/li&gt;
&lt;li&gt;Owner program (with friendly names for known programs)&lt;/li&gt;
&lt;li&gt;Whether it's executable or a data account&lt;/li&gt;
&lt;li&gt;Data size and a hex preview of the first bytes&lt;/li&gt;
&lt;li&gt;Rent epoch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what the output looks like against three different account types:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A wallet account:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6bopgz3wf4lmzp28l5rq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6bopgz3wf4lmzp28l5rq.png" alt="Wallet account" width="800" height="645"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Token Program:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxh6hjlx7zxguwtc6sp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdxh6hjlx7zxguwtc6sp3.png" alt="Toke program" width="800" height="672"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;The System Program itself:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyy88vyc5pq5qkzuforqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyy88vyc5pq5qkzuforqi.png" alt="System Program" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same five fields. Three completely different accounts. Let's build it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;solana-account-explorer
&lt;span class="nb"&gt;cd &lt;/span&gt;solana-account-explorer
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @solana/kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create &lt;code&gt;explorer.mjs&lt;/code&gt; — we use &lt;code&gt;.mjs&lt;/code&gt; for ES module syntax with top-level &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Full Code
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSolanaRpc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/kit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Known program addresses mapped to friendly names&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;KNOWN_PROGRAMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;11111111111111111111111111111111&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;System Program&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Token Program&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TokenzQdsByhw9vBi3wq3kQtj78oYCa9PzPgcGW8K3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Token Program (SPL-2022)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ATokenGPvbdGVqstVQmcLsNZAqeEoctVrGMMXuLYDJ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Associated Token Program&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9xQeWvG816bUx9EPjHmaT23yfBYP5ihLeLSKcsFqrXa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Raydium Program&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JUP4Fb2cqiRUcaTHdrPC8h2gNsszFildan3xnjnZALn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jupiter Aggregator&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DezXAZ8z7PnrnRJjz3wXBoRgixVpdZuvXAfqJ7DKPs1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Magic Eden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BPFLoaderUpgradeab1e11111111111111111111111&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BPF Loader (Program Upgrades)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BPFLoader2111111111111111111111111111111111&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BPF Loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Get address from command line argument&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Error: No address provided!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Usage: node explorer.mjs &amp;lt;solana-address&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Connect to devnet&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;exploreAccount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;🔍 Solana Account Explorer&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;━&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch balance and account info in parallel&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Convert lamports to SOL&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\n📍 Address:\n   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`💰 Balance:\n   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;solBalance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SOL (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; lamports)\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ℹ️  This address does not exist on the blockchain yet.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;━&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Resolve owner to friendly name if known&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ownerName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;KNOWN_PROGRAMS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`👤 Owner:\n   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ownerName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Executable flag&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isExecutable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`⚙️  Executable:\n   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;isExecutable&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yes (Program Account)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No (Data/Wallet Account)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Rent epoch&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`📅 Rent Epoch:\n   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rentEpoch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Data size and hex preview&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`📦 Data:\n   Size: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dataLength&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataLength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displaySize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dataLength&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dataSample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;displaySize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`   First &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;displaySize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes (hex):`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dataSample&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataLength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;displaySize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`   ... (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dataLength&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;displaySize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; more bytes omitted)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`   (empty)\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;━&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;❌ Error exploring account:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;exploreAccount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Run it against any address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Query the System Program&lt;/span&gt;
node explorer.mjs 11111111111111111111111111111111

&lt;span class="c"&gt;# Query the Token Program&lt;/span&gt;
node explorer.mjs TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA

&lt;span class="c"&gt;# Query your own wallet&lt;/span&gt;
node explorer.mjs &amp;lt;your-wallet-address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What the Output Taught Me
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The System Program owns itself — sort of
&lt;/h3&gt;

&lt;p&gt;When I queried &lt;code&gt;11111111111111111111111111111111&lt;/code&gt;, I expected to see the System Program as its own owner. Instead I got &lt;code&gt;NativeLoader&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That threw me until I understood: &lt;code&gt;NativeLoader&lt;/code&gt; is the Solana runtime's built-in loader. It owns the System Program the same way a kernel owns its own system calls. The System Program is baked into the runtime at a level below the normal account ownership model. It's not a program that was deployed — it was compiled into the validator itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key difference from Token Program:&lt;/strong&gt; The Token Program is owned by &lt;code&gt;BPF Loader (Program Upgrades)&lt;/code&gt;, which means it was deployed like any other program and can be upgraded. The System Program cannot — it's part of the protocol.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Token Program is only 48 bytes — but controls everything
&lt;/h3&gt;

&lt;p&gt;Look at the Token Program output: 48 bytes of data. That's it. 48 bytes of what turns out to be a pointer — not the actual bytecode.&lt;/p&gt;

&lt;p&gt;This is because the Token Program uses the upgradeable BPF loader pattern. The 48 bytes in the main account point to a separate &lt;em&gt;program data account&lt;/em&gt; that holds the actual bytecode. The split exists so the program can be upgraded without changing its address.&lt;/p&gt;

&lt;p&gt;This was my first real encounter with indirection on Solana — the account you call isn't always where the code lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  A wallet with 0 bytes of data is a complete account
&lt;/h3&gt;

&lt;p&gt;The wallet output shows &lt;code&gt;Size: 0 bytes&lt;/code&gt; and &lt;code&gt;(empty)&lt;/code&gt; for data. In Web2 terms that looks like a broken record — a row with no content. But on Solana, a wallet doesn't need data. Its entire purpose is to hold lamports. The &lt;code&gt;owner&lt;/code&gt; field (System Program) and the &lt;code&gt;lamports&lt;/code&gt; field are all it needs to function.&lt;/p&gt;

&lt;p&gt;This is the account model in action: the structure is always the same, but some fields are intentionally empty depending on the account's purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Things Worth Noticing in Your Own Queries
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Compare executable accounts vs data accounts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the explorer on a program address and a wallet address back to back. Notice how &lt;code&gt;executable: Yes&lt;/code&gt; vs &lt;code&gt;executable: No&lt;/code&gt; changes everything about what that account does — even though both have identical field structures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Watch the owner field&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every account you'll ever encounter is owned by exactly one program. Query five random addresses and look at what owns them. You'll start recognizing patterns: wallets owned by System Program, tokens owned by Token Program, NFT metadata owned by Metaplex's program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The data field is where all the interesting stuff hides&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hex bytes in the data field aren't random — they're structured binary data that each program knows how to decode. That wall of hex is actually a token supply, or a price oracle, or a governance vote. Decoding that raw data is exactly what Post 3 covers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;KNOWN_PROGRAMS&lt;/code&gt; Map — Why It Matters
&lt;/h2&gt;

&lt;p&gt;One design choice worth explaining: the &lt;code&gt;KNOWN_PROGRAMS&lt;/code&gt; lookup table at the top of the file.&lt;/p&gt;

&lt;p&gt;Without it, the owner field just shows a raw base58 address like &lt;code&gt;BPFLoaderUpgradeab1e11111111111111111111111&lt;/code&gt;. With it, you see &lt;code&gt;BPF Loader (Program Upgrades)&lt;/code&gt; — immediately meaningful.&lt;/p&gt;

&lt;p&gt;This is the same pattern Solana Explorer uses under the hood. As you build on Solana, you'll develop your own mental map of which program addresses do what. This table is a starter version of that map. Add to it as you encounter new programs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Copy the code above into explorer.mjs, then:&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @solana/kit

&lt;span class="c"&gt;# Try these three to see the contrast clearly:&lt;/span&gt;
node explorer.mjs 11111111111111111111111111111111
node explorer.mjs TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
node explorer.mjs SysvarC1ock11111111111111111111111111111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each one will show you the same five fields, completely different values. That contrast is worth seeing with your own eyes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;This explorer shows you the raw bytes in the &lt;code&gt;data&lt;/code&gt; field — but doesn't tell you what they mean. A token mint's data field contains supply, decimals, and mint authority. A governance account's data field contains vote counts and proposal state. All of it is encoded in binary.&lt;/p&gt;

&lt;p&gt;Post 3 covers exactly that: three methods for decoding raw Solana account data, from manual buffer parsing to using Anchor's IDL to using pre-built deserializers. That's where the hex bytes stop being noise and start being information.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series: The Account Model Foundation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: &lt;a href="https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48"&gt;The Account Model Explained&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: Building an Account Explorer &lt;em&gt;(this post)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: Decoding Account Data &lt;em&gt;(coming May 20)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Part 4: System Program Deep Dive &lt;em&gt;(coming May 22)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;What address did you query first? Drop it in the comments — I'd love to see what people are exploring.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part of the &lt;a href="https://www.mlh.com/events/100-days-of-solana/challenges" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; Epoch 1 Writing Challenge.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>writing</category>
    </item>
    <item>
      <title># Week 4: Understanding Solana’s Account Model as a Web2 Developer</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sat, 16 May 2026 21:56:55 +0000</pubDate>
      <link>https://dev.to/lymah/-week-4-understanding-solanas-account-model-as-a-web2-developer-5329</link>
      <guid>https://dev.to/lymah/-week-4-understanding-solanas-account-model-as-a-web2-developer-5329</guid>
      <description>&lt;p&gt;If you come from a Web2 background, Solana’s account model can feel confusing at first.&lt;br&gt;&lt;br&gt;
You might ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why does everything seem to be an account?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In traditional backend development, your application state usually lives in a database like PostgreSQL or MongoDB. Your server controls the logic, updates records, and manages permissions.&lt;/p&gt;

&lt;p&gt;On Solana, things work differently.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;On Solana, &lt;strong&gt;everything is stored inside accounts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of an account as a structured container that holds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data&lt;/li&gt;
&lt;li&gt;Tokens&lt;/li&gt;
&lt;li&gt;Program state&lt;/li&gt;
&lt;li&gt;Ownership information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of a centralized server managing a database, Solana stores application state directly on-chain inside these accounts.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Web2 Analogy
&lt;/h2&gt;

&lt;p&gt;Here’s a simple comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Web2&lt;/th&gt;
&lt;th&gt;Solana&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database row&lt;/td&gt;
&lt;td&gt;Account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend server&lt;/td&gt;
&lt;td&gt;Program&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API endpoint&lt;/td&gt;
&lt;td&gt;Instruction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User session&lt;/td&gt;
&lt;td&gt;Wallet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database update&lt;/td&gt;
&lt;td&gt;Transaction&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you have built a REST API before, you can think of a Solana &lt;strong&gt;program&lt;/strong&gt; as your backend logic.&lt;/p&gt;

&lt;p&gt;But unlike Web2 servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Programs are mostly stateless&lt;/li&gt;
&lt;li&gt;State lives in accounts&lt;/li&gt;
&lt;li&gt;Users sign transactions directly with wallets&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Programs vs Accounts
&lt;/h2&gt;

&lt;p&gt;This is one of the biggest mindset shifts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Programs
&lt;/h3&gt;

&lt;p&gt;Programs contain executable logic.&lt;/p&gt;

&lt;p&gt;They are similar to backend services or server functions.&lt;/p&gt;

&lt;p&gt;However, programs &lt;strong&gt;do not store mutable state internally&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Accounts
&lt;/h3&gt;

&lt;p&gt;Accounts store the actual data.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User profile data&lt;/li&gt;
&lt;li&gt;NFT metadata&lt;/li&gt;
&lt;li&gt;Token balances&lt;/li&gt;
&lt;li&gt;Game state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a Solana program runs, it receives accounts as input, modifies them if allowed, then saves the updated state back on-chain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ownership Matters
&lt;/h2&gt;

&lt;p&gt;Every Solana account has an owner.&lt;/p&gt;

&lt;p&gt;The owner determines which program can modify the account’s data.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A token account is owned by the Solana Token Program&lt;/li&gt;
&lt;li&gt;Your wallet may control the tokens&lt;/li&gt;
&lt;li&gt;But only the token program can modify token balances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates strong security guarantees.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rent and Storage
&lt;/h2&gt;

&lt;p&gt;Unlike traditional databases, blockchain storage is expensive.&lt;/p&gt;

&lt;p&gt;On Solana, accounts must hold enough SOL to remain active. This is called &lt;strong&gt;rent exemption&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can think of it like prepaying for storage space.&lt;/p&gt;

&lt;p&gt;The larger the account data, the more SOL required.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Model Exists
&lt;/h2&gt;

&lt;p&gt;Solana’s account model is designed for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel transaction execution&lt;/li&gt;
&lt;li&gt;High throughput&lt;/li&gt;
&lt;li&gt;Predictable state access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because transactions specify which accounts they will read or write beforehand, Solana can process many transactions simultaneously.&lt;/p&gt;

&lt;p&gt;This is one reason Solana achieves very high performance compared to many blockchains.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example Mental Model
&lt;/h2&gt;

&lt;p&gt;Imagine building a todo app.&lt;/p&gt;

&lt;p&gt;In Web2:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frontend sends request to backend&lt;/li&gt;
&lt;li&gt;Backend updates database&lt;/li&gt;
&lt;li&gt;Database stores todo item&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Solana:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wallet signs transaction&lt;/li&gt;
&lt;li&gt;Transaction calls a program&lt;/li&gt;
&lt;li&gt;Program updates a todo account&lt;/li&gt;
&lt;li&gt;Updated state is stored on-chain&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The blockchain itself becomes the shared database.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The hardest part of learning Solana is not syntax — it is changing how you think about application state.&lt;/p&gt;

&lt;p&gt;Once you understand that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;programs execute logic&lt;/li&gt;
&lt;li&gt;accounts store state&lt;/li&gt;
&lt;li&gt;wallets authorize actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…the architecture starts making much more sense.&lt;/p&gt;

&lt;p&gt;For Web2 developers, Solana development feels less like building a traditional backend and more like designing a distributed state machine.&lt;/p&gt;

&lt;p&gt;And that mindset shift is the key to understanding blockchain development.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>solana</category>
      <category>blockchain</category>
      <category>web2</category>
    </item>
    <item>
      <title>The Solana Account Model Explained: Everything is an Account</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sat, 16 May 2026 21:31:20 +0000</pubDate>
      <link>https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48</link>
      <guid>https://dev.to/lymah/the-solana-account-model-explained-everything-is-an-account-3g48</guid>
      <description>&lt;h2&gt;
  
  
  The Aha Moment
&lt;/h2&gt;

&lt;p&gt;During &lt;strong&gt;Epoch 1 of #100DaysOfSolana&lt;/strong&gt;, I hit a wall. I'd built wallets, sent transactions, decoded data — but nothing clicked until I stopped thinking about Solana like traditional programming and started thinking about it as &lt;strong&gt;a system where everything is an account&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When I started learning Solana, I kept hearing this phrase: "everything is an account." It sounded like philosophy. But then it hit me: &lt;strong&gt;there are literally no users, no wallets, no programs in Solana — only accounts.&lt;/strong&gt; Your wallet is an account. The Token Program is an account. The system clock is an account. Even your SOL balance is just data in an account.&lt;/p&gt;

&lt;p&gt;This single concept unlocks everything else on Solana.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is an Account? The Five-Field Structure
&lt;/h2&gt;

&lt;p&gt;Every account on Solana has exactly five fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;lamports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// Balance (1 SOL = 1 billion lamports)&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mh"&gt;0x00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...],&lt;/span&gt;     &lt;span class="c1"&gt;// Binary data (up to 10 MB)&lt;/span&gt;
  &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;11111111111...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Program that can modify this account&lt;/span&gt;
  &lt;span class="nx"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// Is this a program (true) or data (false)?&lt;/span&gt;
  &lt;span class="nx"&gt;rent_epoch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18446744073709&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// Rent exemption status&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Five fields. Everything on Solana fits into this model.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flv6vpa9v09ezriq3fp5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flv6vpa9v09ezriq3fp5f.png" alt="System Program account showing all 5 fields in Solana Explorer" width="800" height="393"&gt;&lt;/a&gt; &lt;em&gt;This is a real account (System Program) showing all five fields. You'll see this exact structure for every account on Solana.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Understanding Each Field
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;lamports&lt;/strong&gt; - The account's balance in the smallest unit. Accounts must hold enough SOL (lamports) to be "rent-exempt" (roughly $0.02-$2 depending on data size). This prevents network spam. I built a rent calculator and went deeper on storage costs in my &lt;a href="https://dev.to/lymah/from-just-data-to-a-global-database-my-second-week-learning-solana-5f4m"&gt;Week 2 post&lt;/a&gt; if you want the full breakdown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;data&lt;/strong&gt; - Where the actual information lives. For a wallet, this is empty (0 bytes). For a program, this is bytecode. For a token mint, this is token metadata (supply, decimals, authorities). For a sysvar, this is cluster state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;owner&lt;/strong&gt; - The program that has permission to modify this account's data. Your wallet is owned by the System Program (&lt;code&gt;11111111111...&lt;/code&gt;), so only the System Program can transfer your SOL. A token mint is owned by the Token Program, so only it can mint new tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;executable&lt;/strong&gt; - A boolean flag. If &lt;code&gt;true&lt;/code&gt;, this account contains program code and can execute instructions. If &lt;code&gt;false&lt;/code&gt;, it's just a data account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;rent_epoch&lt;/strong&gt; - A legacy field from when Solana charged rent monthly. Now mostly ignored, but technically tracks when rent was last collected.&lt;/p&gt;
&lt;h2&gt;
  
  
  Three Account Types: Wallets, Programs, and Sysvars
&lt;/h2&gt;

&lt;p&gt;Let me show you how the same five-field structure represents completely different things:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetd32mrb1rijet6ijgws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetd32mrb1rijet6ijgws.png" alt="Comparison view showing Wallet vs Program vs Sysvar" width="799" height="105"&gt;&lt;/a&gt;&lt;em&gt;Same five fields, three completely different meanings. This is the power of the account model.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Type 1: Your Wallet (A Data Account)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Owner:      11111111111111111111111111111111  ← System Program
Executable: false                              ← Stores data, not code
Data:       (empty, 0 bytes)
Lamports:   5000000000                         ← Your SOL balance
Rent Epoch: 18446744073709551615 (max)         ← Rent exempt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0d7gjwxyu8hwner1dfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0d7gjwxyu8hwner1dfr.png" alt="A typical wallet account - System Program owned, no data" width="800" height="399"&gt;&lt;/a&gt;&lt;em&gt;This is what your wallet looks like on-chain: owned by System Program, no data, just lamports.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your wallet is a System Program-owned account with empty data. The "balance" is just the &lt;code&gt;lamports&lt;/code&gt; field. That's it.&lt;/p&gt;

&lt;p&gt;Your wallet is a System Program-owned account with empty data. The "balance" is just the &lt;code&gt;lamports&lt;/code&gt; field. That's it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; You don't own your wallet — the System Program does. The System Program enforces the rules that only the private key owner can transfer SOL. Your keypair is the authorization mechanism, not the owner — if this distinction feels fuzzy, I covered how Solana identity and keypairs actually work in my &lt;a href="https://dev.to/lymah/your-solana-address-is-actually-your-ssh-key-understanding-on-chain-identity-a7h"&gt;Week 1 post&lt;/a&gt;. This is what "custody" means on Solana: the program is the enforcer.&lt;/p&gt;
&lt;h3&gt;
  
  
  Type 2: The Token Program (An Executable Account)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Owner:      BPFLoaderUpgradeab1e...          ← BPF Loader (manages programs)
Executable: true                              ← Contains program code
Data:       (36 bytes of bytecode)            ← The actual program logic
Lamports:   ~11 SOL                           ← Program's retained SOL
Rent Epoch: 18446744073709551615              ← Rent exempt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Address: &lt;code&gt;TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk0w23anu01362firhpg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbk0w23anu01362firhpg.png" alt="Token Program showing executable=true, much larger balance" width="800" height="298"&gt;&lt;/a&gt;&lt;em&gt;👆 Notice: executable=true, balance is 11+ SOL, owned by BPF Loader. This is a program account.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Token Program is an executable account. Its &lt;code&gt;data&lt;/code&gt; field contains the program bytecode that defines how tokens work. When you interact with a token (mint, transfer, burn), you're actually calling instructions in this program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; Programs are just data stored in accounts. The Solana runtime reads the &lt;code&gt;executable: true&lt;/code&gt; flag and knows to execute that account's code. This is why upgrading programs is so clean on Solana — you're just swapping the data in an account.&lt;/p&gt;
&lt;h3&gt;
  
  
  Type 3: Clock Sysvar (A System Data Account)
&lt;/h3&gt;

&lt;p&gt;Owner:      Sysvar1111111111111...           ← Sysvar program&lt;br&gt;
Executable: false                             ← Stores data, not code&lt;br&gt;
Data:       (40 bytes of binary time data)    ← Current slot, epoch, timestamp&lt;br&gt;
Lamports:   ~1 SOL                            ← Enough for rent exemption&lt;br&gt;
Rent Epoch: 18446744073709551615              ← Rent exempt&lt;br&gt;
Address: &lt;code&gt;SysvarC1ock11111111111111111111111111111111&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dzjscjh9b1hbfim69rj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dzjscjh9b1hbfim69rj.png" alt="Clock Sysvar showing executable=false, owned by Sysvar program" width="800" height="386"&gt;&lt;/a&gt;&lt;em&gt;This sysvar is readable by every program but not executable. It contains cluster time state.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Matters: Three Real-World Scenarios
&lt;/h2&gt;

&lt;p&gt;The Clock sysvar is a read-only data account containing cluster-wide state. Every program can read the current slot, epoch, and Unix timestamp from this account. It's like &lt;code&gt;/proc/uptime&lt;/code&gt; in Linux — a public system file anyone can read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key insight:&lt;/strong&gt; Sysvars are how programs access external state without making RPC calls. The Solana runtime updates them every slot.&lt;/p&gt;
&lt;h3&gt;
  
  
  Scenario 1: Sending SOL to a Friend
&lt;/h3&gt;

&lt;p&gt;When you send 1 SOL to a friend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your wallet account (owned by System Program) loses 1 billion lamports&lt;/li&gt;
&lt;li&gt;Your friend's account gains 1 billion lamports&lt;/li&gt;
&lt;li&gt;The System Program enforces that only your keypair can authorize this&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both accounts follow the same five-field structure. The only difference is the keypair that can sign for them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Scenario 2: Creating a Token
&lt;/h2&gt;

&lt;p&gt;When someone creates a USDC token:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They invoke the Token Program (an executable account)&lt;/li&gt;
&lt;li&gt;The Token Program creates a new account for the token mint&lt;/li&gt;
&lt;li&gt;That new account has the Token Program as its &lt;code&gt;owner&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The Token Program stores token metadata in the mint account's &lt;code&gt;data&lt;/code&gt; field&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Same five-field structure. Different owner (Token Program instead of System Program).&lt;/p&gt;
&lt;h3&gt;
  
  
  Scenario 3: Reading the Network Time
&lt;/h3&gt;

&lt;p&gt;Inside a program, you might read the current slot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clockSysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CLOCK_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decodedClock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseClockData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clockSysvar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Current slot: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;decodedClock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're just reading the &lt;code&gt;data&lt;/code&gt; field of an account. Solana's runtime automatically updates that account every slot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Permission Model: Who Can Modify What?
&lt;/h2&gt;

&lt;p&gt;Here's the crucial rule: &lt;strong&gt;Only the account's owner program can modify its data.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If &lt;code&gt;owner === SystemProgram&lt;/code&gt;, only the System Program can change this account&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;owner === TokenProgram&lt;/code&gt;, only the Token Program can change this account&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;owner === YourProgram&lt;/code&gt;, only your program can change this account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is Solana's security model. You can't hack someone else's account because you don't own it. You can't create tokens because you don't own the token program. You can only modify accounts your program owns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exception:&lt;/strong&gt; Anyone can send SOL to any wallet. The System Program allows this because receiving SOL doesn't modify the wallet's &lt;code&gt;data&lt;/code&gt; field — it only changes &lt;code&gt;lamports&lt;/code&gt;. Specifically, the System Program says: "Any keypair can increase another account's lamports, but only the keypair owning an account can decrease it."&lt;/p&gt;

&lt;p&gt;That's a Solana transaction at its core — accounts changing state, programs enforcing the rules. If you want to understand how that transaction lifecycle actually works and how it differs from a REST API call, I went deep on it in my &lt;a href="https://dev.to/lymah/how-solana-transactions-are-different-from-rest-api-calls-and-why-it-matters-4lgc"&gt;Week 3 post&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Account Model in Action: Building an Explorer
&lt;/h2&gt;

&lt;p&gt;To prove this, I built a simple account explorer that queries any address and shows all five fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// solana-account-explorer/explorer.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/kit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;inspectAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addressString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addressString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acct&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Address: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;addressString&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Lamports (Balance): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Owner: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Executable: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Data Length: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Rent Epoch: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;rentEpoch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Try it:&lt;/span&gt;
&lt;span class="c1"&gt;// node explorer.mjs 11111111111111111111111111111111&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoq8vrrkify4doo10ove.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkoq8vrrkify4doo10ove.png" alt="Account explorer terminal output showing all 5 fields for 3 different accounts" width="800" height="293"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7j17z5cs2ngvxfhnne9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7j17z5cs2ngvxfhnne9o.png" alt="Account explorer terminal output showing all 5 fields for 3 different accounts" width="800" height="298"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5k9hw9xif14btd5ac43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5k9hw9xif14btd5ac43.png" alt="Account explorer terminal output showing all 5 fields for 3 different accounts" width="800" height="196"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This tool runs the exact same query on System Program, Token Program, and a wallet. Same structure, different values.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Run this on any Solana address, and you'll see those five fields. Every account. Always the same structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Misconceptions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ "Wallets are real objects"
&lt;/h3&gt;

&lt;p&gt;✅ Wallets are just accounts with the System Program as owner and empty data. The "wallet" concept is a UI abstraction.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ "I own my wallet"
&lt;/h3&gt;

&lt;p&gt;✅ The System Program owns your wallet. Your keypair is the authorization mechanism, not the owner. This is actually safer — you can't accidentally delete your wallet.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ "Programs can access any account"
&lt;/h3&gt;

&lt;p&gt;✅ Programs can &lt;em&gt;read&lt;/em&gt; any account (it's all public data), but they can only &lt;em&gt;modify&lt;/em&gt; accounts they own. This is enforced by the Solana runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ "The blockchain stores all your data"
&lt;/h3&gt;

&lt;p&gt;✅ The blockchain stores account states. Your actual data lives in those accounts. Different mental model!&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything Else Is Just Accounts (Seriously)
&lt;/h2&gt;

&lt;p&gt;Once the five-field structure clicks, everything else becomes obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transactions&lt;/strong&gt; - Signed instructions that modify accounts. A transaction is just a request to change the &lt;code&gt;lamports&lt;/code&gt; or &lt;code&gt;data&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programs&lt;/strong&gt; - Executable accounts that enforce the rules for modification. They're the gatekeepers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens&lt;/strong&gt; - Just accounts owned by the Token Program. The token's supply, decimals, authorities? All in the account's &lt;code&gt;data&lt;/code&gt; field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFTs&lt;/strong&gt; - Same structure. Metadata lives in the account's &lt;code&gt;data&lt;/code&gt;. The blockchain isn't storing the image; it's storing a pointer in an account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wallets&lt;/strong&gt; - Accounts owned by System Program with no data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeFi&lt;/strong&gt; - Programs managing accounts representing liquidity pools, positions, swaps. Every protocol is just accounts + programs enforcing rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AMMs&lt;/strong&gt; - Programs that maintain accounts for token reserves and user positions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of it. Accounts and the programs that manage them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Explore it yourself&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://explorer.solana.com" rel="noopener noreferrer"&gt;Solana Explorer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Search for any address&lt;/li&gt;
&lt;li&gt;Look for those five fields&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Try the inspector&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   solana account 11111111111111111111111111111111
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows you a real System Program account.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build your own reader&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// Now you have: lamports, data, owner, executable, rentEpoch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Understand ownership&lt;/strong&gt;
When building a program, remember: you own accounts your program creates. You can modify them. Users' wallets you can't modify (only System Program can). This shapes how you architect everything.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;## The Takeaway&lt;/p&gt;

&lt;p&gt;The Solana Account Model is deceptively simple: &lt;strong&gt;five fields, one permission rule.&lt;/strong&gt; That's the entire foundation.&lt;/p&gt;

&lt;p&gt;Everything on Solana — wallets, programs, tokens, NFTs, liquidity pools, governance, system clocks, rent — fits perfectly into this structure. There's no special case for "smart contracts" or "user accounts." No complexity hidden in different data structures. Just five fields, repeated across millions of accounts, with one rule controlling who can modify them.&lt;/p&gt;

&lt;p&gt;Master this concept, and you've unlocked the mental model for understanding not just Solana, but why Solana is architecturally different from every other blockchain.&lt;/p&gt;

&lt;p&gt;Next time someone says "everything is an account," you'll understand exactly why they're right — and why that matters.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Series: The Account Model Foundation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 1: The Account Model Explained &lt;em&gt;(this post)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Part 2: Building an Account Explorer &lt;em&gt;(coming May 17)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Part 3: Decoding Account Data &lt;em&gt;(coming May 19)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Part 4: System Program Deep Dive &lt;em&gt;(coming May 21)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;What aspect of the account model confused you most? Let me know in the comments — I'll update this post with clarifications!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Part of the &lt;a href="https://www.mlh.com/events/100-days-of-solana/challenges" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; Epoch 1 challenge.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>writing</category>
    </item>
    <item>
      <title>How Solana Transactions Are Different from REST API Calls (And Why It Matters)</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sat, 09 May 2026 17:25:56 +0000</pubDate>
      <link>https://dev.to/lymah/how-solana-transactions-are-different-from-rest-api-calls-and-why-it-matters-4lgc</link>
      <guid>https://dev.to/lymah/how-solana-transactions-are-different-from-rest-api-calls-and-why-it-matters-4lgc</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: Your Mental Model Is Broken
&lt;/h2&gt;

&lt;p&gt;You know how to build APIs. You understand request/response cycles. You know that a successful POST returns 200 OK, and a failed request returns 400 Bad Request. You've never been charged for a 400.&lt;/p&gt;

&lt;p&gt;Solana transactions will break every one of those assumptions.&lt;/p&gt;

&lt;p&gt;Last week, I sent a transaction that failed due to insufficient funds. The validator still charged me a fee. A 400 Bad Request just cost me real money. That's when I realized: I was thinking about blockchain like it was an API, and it's not.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to show you exactly how Solana transactions work, how they differ from REST APIs, and why understanding those differences will make you a better blockchain developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web2: Request/Response
&lt;/h2&gt;

&lt;p&gt;Here's how you think about HTTP today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /transfer
{
  "recipient": "alice",
  "amount": 100
}

→ Process on server
→ Return result

200 OK: Transfer succeeded
400 Bad Request: Invalid amount
401 Unauthorized: Not authenticated
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key properties:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stateless (each request is independent)&lt;/li&gt;
&lt;li&gt;Synchronous (you wait for a response)&lt;/li&gt;
&lt;li&gt;Validated on the server&lt;/li&gt;
&lt;li&gt;Failed requests are free (no charge for 400s)&lt;/li&gt;
&lt;li&gt;No signature needed (the server trusts TLS)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Web3: Signed State Transitions
&lt;/h2&gt;

&lt;p&gt;Solana transactions are fundamentally different. They're not requests to a server. They're cryptographically signed instructions to change on-chain state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Build a transaction&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;recentBlockhash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blockhash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;feePayer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Add an instruction&lt;/span&gt;
&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;SystemProgram&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fromPubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;toPubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lamports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Sign with private key&lt;/span&gt;
&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Send to network&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendRawTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key differences:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stateful (changes on-chain state)&lt;/li&gt;
&lt;li&gt;Asynchronous (sign locally, then wait for validators)&lt;/li&gt;
&lt;li&gt;Validated by the network&lt;/li&gt;
&lt;li&gt;Failed transactions still cost fees&lt;/li&gt;
&lt;li&gt;Must be signed by the payer's private key&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Fee Problem
&lt;/h2&gt;

&lt;p&gt;Here's the biggest shock: &lt;strong&gt;you pay whether the transaction succeeds or fails&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I tried to transfer 3.20 SOL with only 2.20 SOL balance. On an API, that would be a 400:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /transfer
400: Insufficient funds
Cost: $0.00
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Solana:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;solana transfer 2iHFnuzv... 3.2
Error: Insufficient lamports &lt;span class="o"&gt;(&lt;/span&gt;need 3.2, have 2.2&lt;span class="o"&gt;)&lt;/span&gt;
Cost: 0.000005 SOL &lt;span class="o"&gt;(&lt;/span&gt;~&lt;span class="nv"&gt;$0&lt;/span&gt;.0000005 on mainnet&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I got charged for a transaction that didn't execute.&lt;/p&gt;

&lt;p&gt;Why? Because validators did work to process my transaction. They:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deserialized the signed bytes&lt;/li&gt;
&lt;li&gt;Loaded accounts from storage&lt;/li&gt;
&lt;li&gt;Executed the System Program instruction&lt;/li&gt;
&lt;li&gt;Checked preconditions (balance, signatures, etc.)&lt;/li&gt;
&lt;li&gt;Determined it would fail and rejected it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of that costs compute resources. So they charge a fee. It's economically rational, and it's completely different from REST APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Commitment Levels
&lt;/h2&gt;

&lt;p&gt;With HTTP, you get one response: 200 or error. Solana has three.&lt;/p&gt;

&lt;p&gt;When I send a transaction, it moves through these stages:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Processed (~400ms)
&lt;/h3&gt;

&lt;p&gt;A validator included my transaction in a recent block.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for processed... Transaction processed by validator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it means: A single validator saw your transaction. Not yet secured by consensus.&lt;br&gt;
Risk level: High (1-2% chance it gets forked away)&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Confirmed (~800ms)
&lt;/h3&gt;

&lt;p&gt;66%+ of validators voted on the block containing my transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for confirmation from supermajority... Transaction confirmed by network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it means: Network consensus. You won. It's almost impossible to reverse.&lt;br&gt;
Risk level: Extremely low (no confirmed transaction has ever been reversed in Solana's history)&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Finalized (~12s)
&lt;/h3&gt;

&lt;p&gt;At least 31 additional confirmed blocks built on top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Waiting for finalized... Transaction finalized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it means: Irreversible. Like a database commit that's replicated to disk and backed up.&lt;br&gt;
Risk level: Zero&lt;/p&gt;

&lt;p&gt;Compare this to HTTP where you get one response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP 200 OK immediately
↑
Done. State changed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Solana transactions are multi-stage. Different applications wait at different levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast-paced DEX? Wait for "confirmed" (~800ms)&lt;/li&gt;
&lt;li&gt;Payment processor? Wait for "finalized" (~12s)&lt;/li&gt;
&lt;li&gt;Bridge operation? Wait for even more finalization&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Code: I Built This
&lt;/h2&gt;

&lt;p&gt;Here's my transfer tool. I send 0.01 SOL to a recipient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node transfer.mjs 6SSAKkUGpyuB2dg81L4d8USfXjpnUXPX6qwif7bcQKwc 0.01

&lt;span class="o"&gt;============================================================&lt;/span&gt;
  Solana Transfer Tool
&lt;span class="o"&gt;============================================================&lt;/span&gt;

Connected to Solana devnet
   RPC: https://api.devnet.solana.com

Checking balance...
   Balance: 2.253960000 SOL

Amount: 0.01 SOL &lt;span class="o"&gt;(&lt;/span&gt;10,000,000 lamports&lt;span class="o"&gt;)&lt;/span&gt;

Building transaction...
Signing transaction...
Sending to network...
Waiting &lt;span class="k"&gt;for &lt;/span&gt;processed... Transaction processed by validator

Waiting &lt;span class="k"&gt;for &lt;/span&gt;confirmation from supermajority... Transaction confirmed by network

Waiting &lt;span class="k"&gt;for &lt;/span&gt;finalized... Transaction finalized

Transaction successful!

Signature: 4MNtSnAcA2JL3CgwrVqBAoqyhQVpnvog8LKc65MNjKaroTZDj8F8UhpieMnWRLhV6juAbvMypHP92YnaguRmbNGc

View on Solana Explorer:
https://explorer.solana.com/tx/4MNtSnAcA2JL3CgwrVqBAoqyhQVpnvog8LKc65MNjKaroTZDj8F8UhpieMnWRLhV6juAbvMypHP92YnaguRmbNGc?cluster&lt;span class="o"&gt;=&lt;/span&gt;devnet

Final balance: 2.23895 SOL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the Solana equivalent of an HTTP POST. But look at the differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I sign the transaction with my private key (HTTP just uses TLS)&lt;/li&gt;
&lt;li&gt;I wait through three commitment stages (HTTP returns immediately)&lt;/li&gt;
&lt;li&gt;I get a signature instead of a 200 OK (signatures prove finality)&lt;/li&gt;
&lt;li&gt;I can view the transaction on a public explorer (no such thing in Web2)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Secret Weapon: Simulation
&lt;/h2&gt;

&lt;p&gt;Remember how I said failed transactions cost fees?&lt;/p&gt;

&lt;p&gt;There's a way around it: simulation.&lt;/p&gt;

&lt;p&gt;Before you send a transaction, ask the network: "If I send this, what would happen?"&lt;/p&gt;

&lt;p&gt;The network runs your transaction in a sandbox. If it would fail, it tells you. And you don't pay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simulate first (costs nothing)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;simulation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;simulateTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unsignedTx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;simulation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Would fail:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;simulation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Don't send - you just saved a fee!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Safe to send&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the production pattern. Professional Solana apps always simulate before sending. It saves fees and catches errors early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental Model: From Request/Response to State Transitions
&lt;/h2&gt;

&lt;p&gt;Here's how I had to rethink things:&lt;/p&gt;

&lt;h3&gt;
  
  
  Before (HTTP Thinking)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Craft a request
2. Send it
3. Wait for response
4. Response tells me if it worked
5. If it failed, no penalty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After (Solana Thinking)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Build a signed transaction (cryptographic proof)
2. Simulate it (does it work? costs nothing)
3. Send it (if simulation passed)
4. Wait through three commitment stages (processed → confirmed → finalized)
5. If it fails, I still paid the fee (even in a sandbox!)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key shift: &lt;strong&gt;You're not making a request to a server. You're proposing a signed change to global state that thousands of validators must agree on.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Understanding these differences makes you a better blockchain developer because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You'll design better error handling&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can't just check for 200 vs 400&lt;/li&gt;
&lt;li&gt;Must handle async multi-stage confirmations&lt;/li&gt;
&lt;li&gt;Must implement retry logic for transient failures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You'll write more efficient code&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simulate before sending (saves fees)&lt;/li&gt;
&lt;li&gt;Batch multiple operations in one transaction&lt;/li&gt;
&lt;li&gt;Choose the right commitment level for your use case&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You'll make economically rational decisions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every transaction has a cost&lt;/li&gt;
&lt;li&gt;Failed transactions cost the same as successful ones&lt;/li&gt;
&lt;li&gt;This changes how you architect systems&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You'll understand why certain constraints exist&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,232 byte transaction size limit (fits in UDP)&lt;/li&gt;
&lt;li&gt;~90 second blockhash window (network state needs to be fresh)&lt;/li&gt;
&lt;li&gt;Fee charges for failure (validates work done by validators)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;REST APIs lied to you. They made state changes feel free and instantaneous. Solana is honest about it: changing state is work, it takes time, and you pay for it whether it succeeds or fails.&lt;/p&gt;

&lt;p&gt;That honesty is uncomfortable at first. But it makes you a better engineer. You start thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What operations are actually necessary?&lt;/li&gt;
&lt;li&gt;How can I batch work to minimize fees?&lt;/li&gt;
&lt;li&gt;What happens if this fails?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the questions that lead to well-designed systems.&lt;/p&gt;

&lt;p&gt;If you're coming from Web2 and feeling lost in blockchain development, this mental shift is everything. Once you stop thinking of transactions as API calls and start thinking of them as cryptographically signed state changes that require network consensus, everything else clicks into place.&lt;/p&gt;

&lt;p&gt;Want to experiment? Try building a Solana transfer tool yourself. You'll hit the same realizations I did, and that's where real learning happens.&lt;/p&gt;

&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.solana.com/developing/clients/jsonrpc-api" rel="noopener noreferrer"&gt;Solana Transactions Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment" rel="noopener noreferrer"&gt;Transaction Confirmation and Expiration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.solana.com/developing/clients/fees" rel="noopener noreferrer"&gt;Why Failed Transactions Cost Fees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>100daysofsolana</category>
      <category>mlh</category>
      <category>blockchain</category>
      <category>solana</category>
    </item>
    <item>
      <title>I Built a Permissionless On-Chain Agent Training Arena on Solana in 3 Weeks</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sat, 09 May 2026 16:47:46 +0000</pubDate>
      <link>https://dev.to/lymah/i-built-a-permissionless-on-chain-agent-training-arena-on-solana-in-3-weeks-2on2</link>
      <guid>https://dev.to/lymah/i-built-a-permissionless-on-chain-agent-training-arena-on-solana-in-3-weeks-2on2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;What happens when you put AI agent training on a blockchain, and what I wish someone had told me before I started.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built a Solana program that logs AI agent training episodes on-chain, making learning verifiable and tamper-proof. Two agents competed in a 10×10 grid. After 50 episodes, the Q-learning agent's average reward climbed from 0.10 to 6.50+. Every step is immutable on devnet. The primitive works. Here's how I got there and what broke along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Problem Nobody Talks About&lt;/li&gt;
&lt;li&gt;What I Built&lt;/li&gt;
&lt;li&gt;Why Solana — Not a Database&lt;/li&gt;
&lt;li&gt;The Three Instructions&lt;/li&gt;
&lt;li&gt;The Agents Actually Learn&lt;/li&gt;
&lt;li&gt;The Dashboard&lt;/li&gt;
&lt;li&gt;What I Learned&lt;/li&gt;
&lt;li&gt;The Numbers&lt;/li&gt;
&lt;li&gt;What's Next&lt;/li&gt;
&lt;li&gt;Try It&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Here's something that bothered me for a while before I could articulate it clearly.&lt;/p&gt;

&lt;p&gt;When someone tells you their AI agent trained for 10,000 episodes and achieved superhuman performance, you have exactly two options: believe them, or don't. There's no third option. No audit trail. No verifiable history. No way to distinguish an agent that genuinely learned from one that someone hardcoded a lookup table for and called "trained behavior."&lt;/p&gt;

&lt;p&gt;The entire field of agent training runs on trust. And trust, it turns out, is a terrible foundation for an economy that's supposed to be built on agents doing real work.&lt;/p&gt;

&lt;p&gt;I kept thinking about this during the Agentic SWARM Hackathon (&lt;a href="https://swarm.thecanteenapp.com/" rel="noopener noreferrer"&gt;Canteen&lt;/a&gt; × &lt;a href="https://arena.colosseum.org/refresh-session?redirectBack=%2Fhackathon" rel="noopener noreferrer"&gt;Colosseum&lt;/a&gt;, April–May 2026). The question I kept coming back to was: what would it look like if you couldn't fake it? What if every training step left a permanent, public, verifiable mark?&lt;/p&gt;

&lt;p&gt;That's what I spent three weeks trying to build. The result is a small 10×10 grid world with two competing agents. The primitive it demonstrates is not.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;swarm-arena&lt;/strong&gt; is a permissionless on-chain agent training arena built on &lt;a href="https://solana.com/" rel="noopener noreferrer"&gt;Solana&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Two agents compete in a resource-collection grid world powered by Bevy ECS, a Rust game engine that handles the simulation loop efficiently. After every 200 ticks, an episode ends. The scores get SHA256-hashed alongside the episode state, and that hash gets committed to Solana along with both agents' scores and a timestamp.&lt;/p&gt;

&lt;p&gt;Agent reputation accumulates on-chain across episodes. If an agent crosses a score threshold, a SOL reward is released from a vault PDA automatically, no human in the loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0eho8se7fdr2vd867rqq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0eho8se7fdr2vd867rqq.png" alt="swarm-arena ui on chrome" width="800" height="450"&gt;&lt;/a&gt; &lt;em&gt;The live dashboard, two agents competing, 100 episodes committed, Phantom connected.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7dtx7zx3y6k39ekihf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7dtx7zx3y6k39ekihf4.png" alt="swarm-arena ui on icognito" width="800" height="450"&gt;&lt;/a&gt; &lt;em&gt;The live dashboard, two agents competing, 100 episodes committed, Solflare wallet connected.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;The stack:&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rust + Bevy ECS&lt;/strong&gt;: simulation engine. Bevy's ECS architecture made it clean to separate agent components, movement systems, and reward systems&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Anchor (Solana)&lt;/strong&gt;: three on-chain instructions handle everything: agent registration, episode logging, and finalization&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React + @solana/web3.js&lt;/strong&gt;: dashboard polling devnet every 5 seconds, showing transactions as they land&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first devnet transaction landed on April 12, 2026:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feysbto75qk98t3e0vb7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feysbto75qk98t3e0vb7l.png" alt="first devenet transaction" width="800" height="450"&gt;&lt;/a&gt; &lt;em&gt;First confirmed transaction on Solana Explorer, FINALIZED, MAX confirmations&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;38yieCpWNbex4RDEzXw8pEREHYQNswyW9hYBHXZmigLP9FEmp8FSpDAwPNvU3dcZuY5RrUdWRp6EJcjYJUcEoL21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting to that first confirmed transaction took longer than I expected. More on that in a bit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Solana — Not a Database
&lt;/h2&gt;

&lt;p&gt;I got this question a lot during the hackathon, so let me be direct about it.&lt;/p&gt;

&lt;p&gt;You could absolutely log training episodes to Postgres. It's faster, easier, and doesn't require learning about PDAs and discriminators. But you'd lose four things that matter a lot once you start thinking about agents as economic actors rather than software demos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissionless&lt;/strong&gt;. With a database, I control who can write to it. With Solana, anyone with a keypair can call &lt;code&gt;create_agent&lt;/code&gt; and start submitting episodes. No API key, no approval process, no terms of service I can revoke on a whim. The program is deployed; it just runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Censorship-resistant&lt;/strong&gt;. I can't delete your agent's training history. Neither can anyone else. If your agent trained 10,000 episodes and built a reputation, that record lives on Solana's ledger permanently. This matters more than it sounds. In a world where agents are doing real economic work, the entity controlling the training ledger has enormous power over the market.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composable&lt;/strong&gt;. &lt;code&gt;AgentReputation&lt;/code&gt; PDAs are just public Solana accounts. Any other program on the network can read them. A marketplace that wants to rank agents by verified training history, a staking protocol that weights agents by episode count, and a DAO that gates membership by reputation can all be built on top of the same primitive without asking my permission.&lt;/p&gt;

&lt;p&gt;A database gives you storage. Solana gives you a shared, trustless, programmable record of who trained what, when, and how well. Those are different things.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Instructions
&lt;/h2&gt;

&lt;p&gt;The entire on-chain economy runs on three Anchor instructions. Keeping it to three was a deliberate choice. I wanted the primitive to be as minimal as possible so it's easy to build on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;create_agent(name: String)&lt;/code&gt;&lt;br&gt;
This registers an AgentIdentity PDA, a program-derived account that stores the agent's owner pubkey, a name, and the registration timestamp. It's the agent's permanent on-chain identity. The key insight is that the PDA is derived from the owner's pubkey, so only the keypair holder can register that specific identity. It's censorship-resistant by construction.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;log_episode(episode_id, scores, episode_hash)&lt;/code&gt;&lt;br&gt;
This is the core. It writes an &lt;code&gt;EpisodeLog&lt;/code&gt; PDA with the episode results and increments the agent's &lt;code&gt;AgentReputation&lt;/code&gt; PDA, updating &lt;code&gt;total_score&lt;/code&gt; and &lt;code&gt;episodes_played&lt;/code&gt;. Every call is a permanent mark. You can reconstruct an agent's entire training history from the chain.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;finalize_episode(episode_id, score_threshold)&lt;/code&gt;&lt;br&gt;
This is where it gets interesting economically. If the winning score meets a threshold, 0.001 SOL transfers from the &lt;code&gt;RewardVault&lt;/code&gt; PDA to the winner. No human triggers this. The program does it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;finalize_episode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FinalizeEpisode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;episode_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;score_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.episode_log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="py"&gt;.finalized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ArenaError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AlreadyFinalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;winner_score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="py"&gt;.scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="py"&gt;.scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;winner_score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;score_threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ArenaError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ThresholdNotMet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="py"&gt;.finalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reward_lamports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 0.001 SOL&lt;/span&gt;
    &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.reward_vault&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_mut_lamports&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;reward_lamports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.signer&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_mut_lamports&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;reward_lamports&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The integration tests cover all three instructions — 4/4 passing against a local validator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv6azrdzlmecv7gtvzb2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjv6azrdzlmecv7gtvzb2.png" alt="integration tests cover all three instructions" width="800" height="450"&gt;&lt;/a&gt; &lt;em&gt;4/4 integration tests passing. create_agent, log_episode, reputation accumulation, finalize_episode&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Agents Actually Learn
&lt;/h2&gt;

&lt;p&gt;Week 1 built the pipeline. Week 2 added Q-learning to Agent 0.&lt;/p&gt;

&lt;p&gt;The Q-table maps (state, action) pairs to expected rewards. State is the directional bucket to the nearest resource. Action is one of five moves. After each episode, the table updates based on resources collected and whether Agent 0 beat Agent 1.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The learning curve across episodes:&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Episodes&lt;/th&gt;
&lt;th&gt;Agent 0 avg reward&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-10&lt;/td&gt;
&lt;td&gt;0.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11-20&lt;/td&gt;
&lt;td&gt;0.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21-30&lt;/td&gt;
&lt;td&gt;2.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;31-40&lt;/td&gt;
&lt;td&gt;5.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;41-50&lt;/td&gt;
&lt;td&gt;6.50+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;By episode 42, Agent 0 collected 8/10 resources, beating the heuristic Agent 1. Every step of this learning curve is committed to Solana devnet. The blockchain is the training log.&lt;/p&gt;

&lt;p&gt;One of the hackathon organizers asked: &lt;em&gt;"Do you think the agents just memorize the policy given how small the world state is without randomization?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdm8j1ov84uo6d8ypeg9u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdm8j1ov84uo6d8ypeg9u.png" alt="organizer's conversation" width="800" height="238"&gt;&lt;/a&gt;&lt;br&gt;
Yes, with fixed positions and 9 directional states, the agent converges to a memorized lookup table. That's why I switched to randomized resource positions each episode. Slower convergence, but genuine exploration. The same organizer then suggested scaling to Minecraft maps, and that's exactly right. The on-chain primitive is world-agnostic. Any map maker could deploy their world config as a PDA, agents train against it, and reputation accumulates permissionlessly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dashboard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://arena-ui-pi.vercel.app/" rel="noopener noreferrer"&gt;Live at&lt;/a&gt;. Built with React + &lt;code&gt;@solana/web3.js&lt;/code&gt; + Recharts. Shows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrq4x0vj7m9m6t1vufuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrq4x0vj7m9m6t1vufuu.png" alt="agent0 history" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent score totals and win rates across 100 episodes&lt;/li&gt;
&lt;li&gt;10×10 arena grid with agent positions&lt;/li&gt;
&lt;li&gt;Score history chart showing the Q-learning oscillation&lt;/li&gt;
&lt;li&gt;Live transaction feed with Explorer links&lt;/li&gt;
&lt;li&gt;Phantom and Solflare wallet connection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkih719m49izb59ropzn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkih719m49izb59ropzn2.png" alt="agent1 history" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The terminal aesthetic was intentional; this is infrastructure, not a consumer app.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The discriminator mismatch will cost you days&lt;/strong&gt;. Every &lt;code&gt;0x1004&lt;/code&gt; error is an Anchor instruction discriminator mismatch. Compute SHA256 of &lt;code&gt;"global:{instruction_name}"&lt;/code&gt; and take the first 8 bytes. Get this wrong, and nothing works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q-learning on small state spaces converges fast but generalizes poorly&lt;/strong&gt;. If your state space is small enough to memorize, you're doing table lookup, not RL. State space design is the hard part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-chain agent training is a primitive, not a product&lt;/strong&gt;. The 10×10 grid is a proof of concept. The real value is &lt;code&gt;AgentIdentity + EpisodeLog + AgentReputation&lt;/code&gt; as a composable on-chain state that any program can read and build on.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Program ID: &lt;code&gt;CCnPxPLd4GbxycDTcP12KP98rWtjKCCNcZC4hqHCB1KV&lt;/code&gt; (Solana devnet)&lt;/li&gt;
&lt;li&gt;First transaction: April 12, 2026&lt;/li&gt;
&lt;li&gt;Episodes committed: 100+&lt;/li&gt;
&lt;li&gt;Integration tests: 4/4 passing&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/Lymah123/swarm-arena" rel="noopener noreferrer"&gt;swarm-arena&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live dashboard: &lt;a href="https://arena-ui-pi.vercel.app/" rel="noopener noreferrer"&gt;arena-ui&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expand world size to arbitrary grids&lt;/li&gt;
&lt;li&gt;Minecraft map integration: map makers deploy world configs as PDAs&lt;/li&gt;
&lt;li&gt;Multi-operator support: external training loops calling the same program&lt;/li&gt;
&lt;li&gt;Mainnet deployment with real SOL rewards&lt;/li&gt;
&lt;li&gt;Reputation composability: other programs reading &lt;code&gt;AgentReputation&lt;/code&gt; PDAs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The program is deployed on Solana devnet. Anyone can call &lt;code&gt;create_agent&lt;/code&gt; with their own keypair and start submitting episodes. The reputation you accumulate is yours; no central authority can take it away.&lt;/p&gt;

&lt;p&gt;That's the point.&lt;/p&gt;




&lt;p&gt;If you're building in the agent infrastructure space, the &lt;br&gt;
&lt;a href="https://swarm.thecanteenapp.com/" rel="noopener noreferrer"&gt;Canteen&lt;/a&gt; Agentic SWARM Hackathon is where this conversation is happening. The quality of technical feedback from the organizers alone made this worth building.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built during the Agentic SWARM Hackathon by &lt;a href="https://swarm.thecanteenapp.com/" rel="noopener noreferrer"&gt;Canteen&lt;/a&gt; × &lt;a href="https://colosseum.com/" rel="noopener noreferrer"&gt;Colosseum&lt;/a&gt;, April–May 2026. &lt;br&gt;
Stack: Rust, Bevy ECS, Anchor, React, Solana devnet. Find me in the &lt;a href="https://discord.gg/canteen" rel="noopener noreferrer"&gt;Canteen Discord&lt;/a&gt; if you want to run your own agent against the program.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>solana</category>
      <category>ai</category>
      <category>bevy</category>
      <category>rust</category>
    </item>
    <item>
      <title>From "Just Data" to "A Global Database": My Second Week Learning Solana</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sat, 02 May 2026 22:03:47 +0000</pubDate>
      <link>https://dev.to/lymah/from-just-data-to-a-global-database-my-second-week-learning-solana-5f4m</link>
      <guid>https://dev.to/lymah/from-just-data-to-a-global-database-my-second-week-learning-solana-5f4m</guid>
      <description>&lt;p&gt;&lt;em&gt;Day 13 of 100 Days of Solana — Reflecting on Reading On-Chain Data&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When I started this challenge two weeks ago with  Major League Hacking(MLH), I thought blockchain was just a ledger with transactions. I was wrong—and that realization is worth writing about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Expectation vs. Reality
&lt;/h2&gt;

&lt;p&gt;Before I touched any code, I imagined blockchain data like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transactions sit in a database somewhere&lt;/li&gt;
&lt;li&gt;I'd need special permission to read them&lt;/li&gt;
&lt;li&gt;The data would be siloed, private by default&lt;/li&gt;
&lt;li&gt;Maybe I'd need to run a full node myself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I actually found:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I could query a random program address on the internet and instantly see its balance, owner, executable flag, and transaction history. No API key. No permission. No credentials. Just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lamports&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 2,500,000,000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That moment when I ran &lt;code&gt;npm run inspect&lt;/code&gt; and saw my account details appear instantly? That was the click.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Moment It Clicked: "Public By Default"
&lt;/h2&gt;

&lt;p&gt;On Day 11, I created a comparison table between traditional databases and Solana accounts. The row that hit me hardest was:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Visibility&lt;/th&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Solana&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Private by default; you choose what to expose&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Public by default; anyone can read any account's data&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In Web2, I'm used to databases being fortresses. You build walls, then decide who gets in. On Solana, every account is an open book the moment it exists. The security model is completely inverted.&lt;/p&gt;

&lt;p&gt;This isn't a bug. It's the feature.&lt;/p&gt;

&lt;p&gt;When I queried the same address on both devnet and mainnet on Day 12, I saw:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Devnet&lt;/strong&gt;: 0.001159846 SOL, recent transactions, fresh account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mainnet&lt;/strong&gt;: 0.069875097 SOL, different transaction history, independent state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same address. Same blockchain. Different networks. Different data.&lt;/p&gt;

&lt;p&gt;It finally made sense: this isn't one database. This is &lt;strong&gt;multiple distributed ledgers&lt;/strong&gt;, all queryable in the same way, all public by design.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Most Surprising Thing: No JOINs
&lt;/h2&gt;

&lt;p&gt;When I learned that Solana programs can't query each other—that there are no JOINs, no server-side filtering, no SQL—I thought it was a limitation.&lt;/p&gt;

&lt;p&gt;It's not.&lt;/p&gt;

&lt;p&gt;It's a completely different mental model.&lt;/p&gt;

&lt;p&gt;In traditional APIs, I'd hit &lt;code&gt;/users/123/posts&lt;/code&gt; and the server would JOIN the users table with the posts table. Solana doesn't work that way. Programs receive accounts as inputs. If I want to "query" related data, I fetch it on the client and assemble it myself.&lt;/p&gt;

&lt;p&gt;At first, this felt clunky. Then I realized: this is why Solana is so fast. There's no server deciding how to optimize my query. I get the raw data and compose it however I need.&lt;/p&gt;

&lt;h2&gt;
  
  
  RPC Calls vs. Traditional APIs
&lt;/h2&gt;

&lt;p&gt;I've worked with REST APIs for years. You know the pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make a request&lt;/li&gt;
&lt;li&gt;Wait for the server to think&lt;/li&gt;
&lt;li&gt;Get back JSON&lt;/li&gt;
&lt;li&gt;Hope it has what you need&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;RPC calls feel different. Faster. Simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSignaturesForAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns transaction signatures. Not a pretty dashboard. Not a summary. Just the data. The API surface is thin. The results are raw.&lt;/p&gt;

&lt;p&gt;It's refreshing. It forces me to think about what I actually need, rather than what the API decided to give me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rent Model: Storage That Costs Real Money
&lt;/h2&gt;

&lt;p&gt;This blew my mind: on Solana, storage costs are explicit and tied to your account.&lt;/p&gt;

&lt;p&gt;On Day 11, I built a rent calculator. For a 0-byte account: ~890,880 lamports. For a 1000-byte account: ~1,874,880 lamports. The cost scales linearly with data size.&lt;/p&gt;

&lt;p&gt;In Web2, storage is abstracted. You pay a monthly AWS bill, somewhere in a spreadsheet. On Solana, you pay per byte. Every byte is yours to manage.&lt;/p&gt;

&lt;p&gt;This is radical. And terrifying. And smart.&lt;/p&gt;

&lt;p&gt;It means you think about data efficiency. It means bloat has consequences. It means storage is a feature, not an externality.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Still Confuses Me
&lt;/h2&gt;

&lt;p&gt;✗ &lt;strong&gt;Program Derived Addresses (PDAs)&lt;/strong&gt; — I see them mentioned constantly but haven't built one yet&lt;br&gt;&lt;br&gt;
✗ &lt;strong&gt;Cross-program invocations (CPIs)&lt;/strong&gt; — How do programs call other programs safely?&lt;br&gt;&lt;br&gt;
✗ &lt;strong&gt;The difference between accounts and wallets&lt;/strong&gt; — I understand the technical difference, but the mental model still feels fuzzy&lt;br&gt;&lt;br&gt;
✗ &lt;strong&gt;Rent refunds&lt;/strong&gt; — When I close an account, the lamports come back... to where?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Clicked This Week
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Accounts are objects&lt;/strong&gt;. Not records in a table. Not documents in a database. Objects with data, an owner, and permissions.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Public data is the default&lt;/strong&gt;. Not something you have to enable. Something you have to be intentional about hiding.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Same address ≠ same account across networks&lt;/strong&gt;. Devnet, testnet, mainnet are completely separate blockchains. Your address looks the same; the state is different.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;"Reading on-chain data" is just fetching&lt;/strong&gt;. No joins. No transactions. No consistency guarantees (beyond finality). Just: "Give me this account's data."&lt;/p&gt;

&lt;h2&gt;
  
  
  For Next Week
&lt;/h2&gt;

&lt;p&gt;I've spent two weeks reading. Next week, I want to &lt;em&gt;write&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not transactions. Not messages. I want to write data to the blockchain using my own program. I want to understand what happens when I create an account, store something in it, and then modify it.&lt;/p&gt;

&lt;p&gt;I want to feel, in my fingers, what it means that the runtime enforces: "Only your program can modify your data."&lt;/p&gt;




&lt;h2&gt;
  
  
  The Big Picture
&lt;/h2&gt;

&lt;p&gt;Two weeks ago, I thought blockchain was a ledger. Now I think it's a database—a weird one, by Web2 standards, but a database nonetheless.&lt;/p&gt;

&lt;p&gt;The rules are different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No schema&lt;/li&gt;
&lt;li&gt;No server&lt;/li&gt;
&lt;li&gt;No private data&lt;/li&gt;
&lt;li&gt;No join operations&lt;/li&gt;
&lt;li&gt;Storage costs are explicit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It feels strange. It feels broken in ways I don't have names for yet.&lt;/p&gt;

&lt;p&gt;But it also feels true. And transparent. And distributed.&lt;/p&gt;

&lt;p&gt;And that's worth building on.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What surprised you most when you started learning Solana? Drop a comment—I'd love to know.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Catch-up on week 1 &lt;a href="https://dev.to/lymah/your-solana-address-is-actually-your-ssh-key-understanding-on-chain-identity-a7h"&gt;write-up&lt;/a&gt; here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building in public. Learning in public. Days 1-13 of 100.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>mlh</category>
      <category>blockchain</category>
      <category>solana</category>
    </item>
    <item>
      <title>Traditional Database vs Solana Accounts Comparison</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Thu, 30 Apr 2026 17:30:20 +0000</pubDate>
      <link>https://dev.to/lymah/traditional-database-vs-solana-accounts-comparison-8k2</link>
      <guid>https://dev.to/lymah/traditional-database-vs-solana-accounts-comparison-8k2</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Traditional Database&lt;/th&gt;
&lt;th&gt;Solana Accounts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Location&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rows in tables on a centralized server&lt;/td&gt;
&lt;td&gt;Accounts on a distributed ledger across validators&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Defined by the database (SQL DDL, document schema)&lt;/td&gt;
&lt;td&gt;Defined by the owning program; stored as raw bytes in the account's data field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access Control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application-level auth (SQL roles, app middleware)&lt;/td&gt;
&lt;td&gt;Enforced by the runtime: only the owning program can modify an account, and only with the required signer(s)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost of Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Server/cloud hosting fees, pay for disk space&lt;/td&gt;
&lt;td&gt;Rent-exempt deposit proportional to data size; refundable when the account is closed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity/Keys&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auto-increment IDs, UUIDs&lt;/td&gt;
&lt;td&gt;32-byte public keys or Program Derived Addresses (PDAs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQL queries, document lookups&lt;/td&gt;
&lt;td&gt;RPC calls (getAccountInfo, getProgramAccounts)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Writes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;INSERT/UPDATE via application code&lt;/td&gt;
&lt;td&gt;Transactions with instructions, signed by authorized keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code vs Data&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application code and database are separate systems&lt;/td&gt;
&lt;td&gt;Both are accounts; programs (code) and data accounts coexist in the same model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deletion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DELETE query removes the row&lt;/td&gt;
&lt;td&gt;Close the account, lamports are returned to you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Private by default; you choose what to expose&lt;/td&gt;
&lt;td&gt;Public by default; anyone can read any account's data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Differences
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Data Location
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: Data lives on centralized servers you control or pay someone to host&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana&lt;/strong&gt;: Data is replicated across thousands of validator nodes worldwide&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Access Control
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: Enforced by application logic and database permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana&lt;/strong&gt;: Enforced at the protocol level - a program cannot modify an account unless it owns it&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Storage Costs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: Monthly hosting fees for infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana&lt;/strong&gt;: Direct lamport deposit tied to each account - rent-exempt deposit for permanent storage&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Querying
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: JOIN operations, server-side filtering, complex queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana&lt;/strong&gt;: &lt;strong&gt;NO JOINS!&lt;/strong&gt; Programs receive accounts as inputs. Off-chain queries via RPC, then client-side assembly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Transparency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: Private by default, access controlled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana&lt;/strong&gt;: Public by default, anyone can read any account, anywhere, anytime&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Web2 → Web3 Thinking
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Web2&lt;/th&gt;
&lt;th&gt;Web3 / Solana&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database row = unique record ID (auto-increment, UUID)&lt;/td&gt;
&lt;td&gt;Solana account = unique public key (32 bytes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication = username + password&lt;/td&gt;
&lt;td&gt;Authentication = secret key signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment = monthly invoice&lt;/td&gt;
&lt;td&gt;Payment = transaction fees (in lamports)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data privacy = encryption + access control&lt;/td&gt;
&lt;td&gt;Data privacy = cryptographic keys (account is public, but only signer can modify)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup = database replication&lt;/td&gt;
&lt;td&gt;Backup = globally distributed ledger&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admin changes data&lt;/td&gt;
&lt;td&gt;Program changes data (signed instruction)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>100daysofsolana</category>
      <category>mlh</category>
      <category>blckchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>Your Solana Address Is Actually Your SSH Key: Understanding On-Chain Identity</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sat, 25 Apr 2026 15:11:24 +0000</pubDate>
      <link>https://dev.to/lymah/your-solana-address-is-actually-your-ssh-key-understanding-on-chain-identity-a7h</link>
      <guid>https://dev.to/lymah/your-solana-address-is-actually-your-ssh-key-understanding-on-chain-identity-a7h</guid>
      <description>&lt;p&gt;If you've ever managed a server, you know SSH keys. You generate a keypair, stick the public key on a server, and suddenly you can prove who you are by signing requests with your private key. The server doesn't care about your username—it cares that you can prove you hold the private key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solana identity works the exact same way. And that's the entire revolution.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Web2 Identity Problem
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://www.investopedia.com/terms/w/web-20.asp" rel="noopener noreferrer"&gt;Web2&lt;/a&gt;, your identity is scattered everywhere. You have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A username and password on GitHub&lt;/li&gt;
&lt;li&gt;A different one on your bank&lt;/li&gt;
&lt;li&gt;An email address (that technically belongs to your email provider)&lt;/li&gt;
&lt;li&gt;A phone number that the telecom company controls&lt;/li&gt;
&lt;li&gt;Social media profiles run by Meta, Google, and X(formerly known as Twitter)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each one is a separate identity. Each one can be reset, locked, or deleted by the service provider. If GitHub's databases get hacked, your account gets compromised. If you forget your password, you plead with support to reset it. You don't actually &lt;em&gt;own&lt;/em&gt; any of these identities—you're renting them.&lt;/p&gt;

&lt;p&gt;The problem gets worse: &lt;strong&gt;none of these identities talk to each other&lt;/strong&gt;. When you sign into a new app, you have to create a new account. That app might offer "Sign in with Google," but that's just a bridge—Google still controls the relationship. You still don't own the identity; Google does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the Keypair
&lt;/h2&gt;

&lt;p&gt;A Solana keypair is fundamentally different. It's two mathematically linked pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Public key&lt;/strong&gt; (your address, like &lt;code&gt;9EPnCtdDoYt9...&lt;/code&gt;): Anyone can see this. It's like putting your public SSH key on every server simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private key&lt;/strong&gt;: Only you have this. Sign something with your private key, and anyone can verify you signed it using your public key—without ever seeing the private key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the mind-blowing part: &lt;strong&gt;Nobody issued this keypair to you. You generated it yourself. You own it completely.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You didn't fill out a form. You didn't wait for approval. You ran &lt;code&gt;solana-keygen new&lt;/code&gt;, and boom—you have a cryptographic identity that's yours forever. No company can lock you out. No database breach can steal it (unless you were careless with the file). No CEO can revoke it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Enables
&lt;/h2&gt;

&lt;p&gt;Once you have a keypair, suddenly you can:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Own accounts on a blockchain:&lt;/strong&gt; Instead of a username on GitHub's servers, your public key &lt;em&gt;is&lt;/em&gt; your identity on Solana. Any wallet, any dApp, any program knows you by your address. Your identity is portable—use it everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign transactions:&lt;/strong&gt; Want to move SOL from your account? You sign it with your private key. Want to vote in a DAO? Sign it. Want to approve a smart contract to spend your tokens? Sign it. The Solana network verifies the signature using your public key and executes the transaction. No permission slip needed from a company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prove ownership cryptographically:&lt;/strong&gt; In Web2, you prove you're you by typing a password into their login form. On Solana, you prove it by signing something. The difference: a password can be stolen or brute-forced. A cryptographic signature tied to your keypair is mathematically unbreakable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Own your data on-chain:&lt;/strong&gt; Every transaction, every token you hold, every program you interact with—it's all tied to your address. Your financial history is on the blockchain, linked to &lt;em&gt;your&lt;/em&gt; identity, not a company's database. You can take this history anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tradeoff
&lt;/h2&gt;

&lt;p&gt;There's a catch. &lt;strong&gt;With great ownership comes great responsibility.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you lose your private key, it's gone forever. No "forgot your password" button. No customer support. This is why Wallet apps give you a seed phrase—12 or 24 words that let you recover your keypair if needed. Lose the seed phrase &lt;em&gt;and&lt;/em&gt; the private key, and your account is genuinely locked forever.&lt;/p&gt;

&lt;p&gt;In Web2, the company absorbs the risk of losing your account. On Solana, you do. That's the tradeoff for true ownership.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Web2 Developers
&lt;/h2&gt;

&lt;p&gt;You already use SSH keys. You understand the power of public key &lt;a href="https://blog.cfte.education/what-is-cryptography-in-blockchain/" rel="noopener noreferrer"&gt;cryptography&lt;/a&gt;—that's why you use it to authenticate to GitHub and deploy servers without passwords. Solana is taking that exact concept and making it the foundation of identity and ownership across an entire network.&lt;/p&gt;

&lt;p&gt;Every dApp, every protocol, every contract you interact with uses the same identity model. Your address is always &lt;em&gt;yours&lt;/em&gt;. Nobody can freeze it, lock it, or take it away. The blockchain doesn't have a support team or a privacy policy—it has math.&lt;/p&gt;

&lt;p&gt;This is why people say blockchain enables true digital ownership. It's not hype. It's SSH keys everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Your Solana address is your identity now. Every time you interact with Solana—whether it's checking your balance, sending a transaction, or approving a smart contract—you're using your keypair to prove who you are.&lt;/p&gt;

&lt;p&gt;You've already generated one. You've already used it to check your wallet balance and see your transaction history. You own an on-chain identity now.&lt;/p&gt;

&lt;p&gt;Welcome to Solana.&lt;/p&gt;




&lt;h3&gt;
  
  
  Resouces
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://solana.com/docs/core/accounts" rel="noopener noreferrer"&gt;Solana Docs: Accounts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/docs/core/pda" rel="noopener noreferrer"&gt;Solana Docs: Program Derived Addresses (PDAs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://solana.com/developers/cookbook/wallets/create-keypair" rel="noopener noreferrer"&gt;Solana Cookbook: How to Create a Keypair&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sns.guide/" rel="noopener noreferrer"&gt;Solana Name Service Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow for more, and you can connect with me &lt;a href="https://github.com/Lymah123" rel="noopener noreferrer"&gt;here&lt;/a&gt; as well.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>solana</category>
    </item>
    <item>
      <title>From Localhost to Production: Deploying an AI Blog Generator (FastAPI + React)</title>
      <dc:creator>Lymah</dc:creator>
      <pubDate>Sun, 25 Jan 2026 18:39:41 +0000</pubDate>
      <link>https://dev.to/lymah/from-localhost-to-production-deploying-an-ai-blog-generator-fastapi-react-4m1n</link>
      <guid>https://dev.to/lymah/from-localhost-to-production-deploying-an-ai-blog-generator-fastapi-react-4m1n</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 2 of building an AI blog generator with FastAPI, React, Hugging Face, Railway, and Vercel.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This post is for developers who’ve built full-stack apps locally and want to confidently ship them to production without overengineering.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6tmmo44b5bq8t56wcxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6tmmo44b5bq8t56wcxg.png" alt="Deployment success" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo96jwlun9uf4zg17s21j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo96jwlun9uf4zg17s21j.png" alt="frontent-vercel" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Welcome Back!
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/lymah/building-an-ai-blog-generator-with-fastapi-react-and-hugging-face-49m5"&gt;Part 1&lt;/a&gt;, I built an AI-powered blog generator locally.&lt;br&gt;
In this post, I’ll show how I deployed the full system to production using Railway and Vercel—covering backend deployment, database setup, frontend integration, and the real issues I ran into along the way.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploying the FastAPI backend to Railway&lt;/li&gt;
&lt;li&gt;Setting up PostgreSQL in production&lt;/li&gt;
&lt;li&gt;Deploying the React frontend to Vercel&lt;/li&gt;
&lt;li&gt;Connecting everything together&lt;/li&gt;
&lt;li&gt;The challenges I faced and how I solved them&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://frontend-2t151l0f1-lymahs-projects.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Lymah123/ai-blog-generator?tab=readme-ov-file" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Let's dive in! 
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Deployment Strategy
&lt;/h2&gt;

&lt;p&gt;Before jumping into deployment, I needed to make some key decisions.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why Railway for Backend?
&lt;/h3&gt;

&lt;p&gt;I chose &lt;strong&gt;Railway&lt;/strong&gt; over alternatives like Heroku, AWS, or DigitalOcean because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL Integration&lt;/strong&gt; - One-click database setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Integration&lt;/strong&gt; - Automatic deployments on push&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Experience&lt;/strong&gt; - Clean UI, clear logs, easy configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing&lt;/strong&gt; - $5/month hobby plan is perfect for side projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Config&lt;/strong&gt; - Railway auto-detects Python and handles everything&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Why Vercel for Frontend?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Vercel&lt;/strong&gt; was the obvious choice for the React frontend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Built for React/Vite&lt;/strong&gt; - Optimized for frontend frameworks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global CDN&lt;/strong&gt; - Lightning-fast content delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic HTTPS&lt;/strong&gt; - SSL certificates out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preview Deployments&lt;/strong&gt; - Every PR gets its own URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free Tier&lt;/strong&gt; - Perfect for personal projects&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Architecture Overview
&lt;/h3&gt;

&lt;p&gt;Here's what we're building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐     HTTPS      ┌─────────────┐
│             │◄───────────────►│             │
│   Vercel    │                 │   Railway   │
│  (Frontend) │                 │  (Backend)  │
│             │                 │             │
└─────────────┘                 └──────┬──────┘
                                       │
                                       │
                                ┌──────▼──────┐
                                │ PostgreSQL  │
                                │  Database   │
                                └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, scalable, and cost-effective.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1: Deploying Backend to Railway
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Preparing the Backend
&lt;/h3&gt;

&lt;p&gt;Before deployment, I needed to ensure the backend was production-ready.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating railway.json
&lt;/h4&gt;

&lt;p&gt;Railway uses this file to understand how to deploy our app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://railway.app/railway.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"builder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NIXPACKS"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"startCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvicorn app.main:app --host 0.0.0.0 --port $PORT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"restartPolicyType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ON_FAILURE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"restartPolicyMaxRetries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NIXPACKS&lt;/code&gt; auto-detects Python and installs dependencies&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--host 0.0.0.0&lt;/code&gt; allows external connections&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$PORT&lt;/code&gt; uses Railway's dynamic port assignment&lt;/li&gt;
&lt;li&gt;Restart policy handles crashes gracefully&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Updating Production Dependencies
&lt;/h4&gt;

&lt;p&gt;Made sure &lt;code&gt;requirements.txt&lt;/code&gt; included production essentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi==0.104.1
uvicorn[standard]==0.24.0
sqlalchemy==2.0.23
psycopg2-binary==2.9.9  # PostgreSQL driver
pydantic==2.5.0
pydantic-settings==2.1.0
python-dotenv==1.0.0
requests==2.31.0
gunicorn==21.2.0  # Production server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Setting Up Railway
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Creating the Project
&lt;/h4&gt;

&lt;p&gt;The process was surprisingly smooth:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Went to &lt;a href="https://railway.app" rel="noopener noreferrer"&gt;railway.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Clicked "Start a New Project"&lt;/li&gt;
&lt;li&gt;Selected "Deploy from GitHub repo"&lt;/li&gt;
&lt;li&gt;Chose my &lt;code&gt;ai-blog-generator&lt;/code&gt; repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Important:&lt;/strong&gt; Selected &lt;code&gt;backend&lt;/code&gt; as the root directory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Railway immediately started building!&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding PostgreSQL
&lt;/h4&gt;

&lt;p&gt;This was the easiest database setup I've ever done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Click "New" → "Database" → "PostgreSQL"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Railway automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provisioned a PostgreSQL instance&lt;/li&gt;
&lt;li&gt;Created the &lt;code&gt;DATABASE_URL&lt;/code&gt; environment variable&lt;/li&gt;
&lt;li&gt;Connected it to my backend service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;No configuration files, no manual connection strings - just magic.&lt;/strong&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Environment Variables
&lt;/h3&gt;

&lt;p&gt;Railway makes environment management clean and secure.&lt;/p&gt;

&lt;p&gt;I added these variables via the Railway dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HUGGINGFACE_API_KEY=hf_xxxxxxxxxxxxx
HUGGINGFACE_MODEL=meta-llama/Llama-3.3-70B-Instruct
ENVIRONMENT=production
APP_NAME=AI Blog Generator API
VERSION=1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; The &lt;code&gt;DATABASE_URL&lt;/code&gt; was automatically set when I added PostgreSQL. No manual configuration needed!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: First Deployment Attempt
&lt;/h3&gt;

&lt;p&gt;I pushed my code and watched the build logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Prepare for Railway deployment"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Railway detected the push and started building...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it failed.&lt;/strong&gt; 😅&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge #1: Import Errors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ModuleNotFoundError: No module named 'app.database'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My local environment had different import paths than production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I realized I needed to ensure all imports were relative to the &lt;code&gt;backend&lt;/code&gt; directory. Updated &lt;code&gt;app/main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before (worked locally)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;

&lt;span class="c1"&gt;# After (works everywhere)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.database&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; Always test imports from the project root!&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge #2: Database Tables Not Created
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;API worked, but generating blogs failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;relation "blog_posts" does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Cause:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FastAPI wasn't creating tables on startup in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Updated the lifespan context manager in &lt;code&gt;main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;

&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Startup: Create tables
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Creating database tables...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Tables created successfully!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="c1"&gt;# Shutdown
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Application shutting down&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now tables are created automatically on every deployment!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Generating a Domain
&lt;/h3&gt;

&lt;p&gt;Railway provides a free subdomain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings → Networking → Generate Domain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Got: &lt;code&gt;https://ai-blog-generator-production-bd0c.up.railway.app&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Tested it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl curl https://ai-blog-generator-production-bd0c.up.railway.app/health

&lt;span class="c"&gt;# Response:&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;:&lt;span class="s2"&gt;"healthy"&lt;/span&gt;,&lt;span class="s2"&gt;"environment"&lt;/span&gt;:&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend deployed successfully!&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop1e63qlmo7usqskd0js.png" alt="backend deployment logs" width="800" height="370"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Part 2: Deploying Frontend to Vercel
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Preparing the Frontend
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Environment Configuration
&lt;/h4&gt;

&lt;p&gt;Created &lt;code&gt;.env.production&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_API_URL=https:/https://ai-blog-generator-production-bd0c.up.railway.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells the frontend where to find the backend in production.&lt;/p&gt;

&lt;h4&gt;
  
  
  Vercel Configuration
&lt;/h4&gt;

&lt;p&gt;Created &lt;code&gt;vercel.json&lt;/code&gt; for optimal deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buildCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"outputDirectory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"framework"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rewrites"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/(.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/index.html"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rewrites&lt;/code&gt; section ensures React Router works properly in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Deploying to Vercel
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Import from GitHub
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Logged into &lt;a href="https://vercel.com" rel="noopener noreferrer"&gt;vercel.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Clicked "New Project"&lt;/li&gt;
&lt;li&gt;Selected my repository&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Critical:&lt;/strong&gt; Set root directory to &lt;code&gt;frontend&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Build Configuration
&lt;/h4&gt;

&lt;p&gt;Vercel auto-detected everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Framework: Vite
Build Command: npm run build
Output Directory: dist
Install Command: npm install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Adding Environment Variable
&lt;/h4&gt;

&lt;p&gt;Added the most important variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VITE_API_URL = https://ai-blog-generator-production-bd0c.up.railway.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clicked "Deploy" and waited...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First deployment: Success!&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Got URL: &lt;code&gt;https://frontend-ochre-rho-71.vercel.app/&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge #3: CORS Errors
&lt;/h3&gt;

&lt;p&gt;Opened my Vercel URL and... nothing worked. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The browser console showed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access to fetch at 'https://ai-blog-generator-production-bd0c.up.railway.app/api/v1/generate' 
from origin 'https://frontend-ochre-rho-71.vercel.app' has been blocked by CORS policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The backend wasn't configured to accept requests from the Vercel domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Added &lt;code&gt;CORS_ORIGINS&lt;/code&gt; to Railway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CORS_ORIGINS=https://frontend-ochre-rho-71.vercel.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Railway auto-redeployed the backend with new CORS settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hard refreshed the frontend (Ctrl+Shift+R) and...&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Connected!&lt;/strong&gt; The status indicator turned green!&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4dwq941o285fzxjdiyrk.png" alt="frontend deployment dashboard" width="800" height="337"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Testing in Production
&lt;/h2&gt;
&lt;h3&gt;
  
  
  First Blog Generation
&lt;/h3&gt;

&lt;p&gt;Held my breath and tried generating a blog:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Topic: The Future of Cloud Computing
Tone: Professional
Length: Medium
Keywords: cloud, scalability, innovation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Clicked "Generate Blog Post"...&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;20 seconds later...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Title: The Future of Cloud Computing: Embracing Innovation and Scalability

Word Count: 1,247 words
SEO Score: 82

[Full formatted blog content displayed]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything worked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ AI generation&lt;/li&gt;
&lt;li&gt;✅ Database storage&lt;/li&gt;
&lt;li&gt;✅ SEO scoring&lt;/li&gt;
&lt;li&gt;✅ Copy to clipboard&lt;/li&gt;
&lt;li&gt;✅ Download as Markdown&lt;/li&gt;
&lt;li&gt;✅ Blog history&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Performance Testing
&lt;/h3&gt;

&lt;p&gt;I generated several blogs to test:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response Times:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API health check: ~50ms&lt;/li&gt;
&lt;li&gt;Blog generation: 25-35 seconds (AI processing)&lt;/li&gt;
&lt;li&gt;Blog retrieval: ~100ms&lt;/li&gt;
&lt;li&gt;Blog list: ~150ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tables created automatically ✅&lt;/li&gt;
&lt;li&gt;Blogs saving correctly ✅&lt;/li&gt;
&lt;li&gt;Queries performing well ✅&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Challenges &amp;amp; Solutions Summary
&lt;/h2&gt;

&lt;p&gt;Let me share all the issues I encountered and how I solved them:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Module Import Errors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;app.database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Ensured all imports were absolute from the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.database&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.models.blog&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BlogPost&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.services.ai_service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hf_service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Database Tables Not Created
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psycopg2.errors.UndefinedTable: relation "blog_posts" does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Added lifespan context manager to create tables on startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lifespan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. CORS Policy Blocking Requests
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access to fetch has been blocked by CORS policy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Added frontend URL to &lt;code&gt;CORS_ORIGINS&lt;/code&gt; in Railway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CORS_ORIGINS=https://ai-blog-generator.vercel.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Environment Variables Not Loading
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
Frontend couldn't connect to backend (showing localhost URL)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Ensured &lt;code&gt;VITE_API_URL&lt;/code&gt; was set in Vercel's environment variables for &lt;strong&gt;all environments&lt;/strong&gt; (Production, Preview, Development).&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Hugging Face API 503 Errors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Model is currently loading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
This is expected! Hugging Face models go to sleep after inactivity. My retry logic handles it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model loading... waiting 30 seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;  &lt;span class="c1"&gt;# Retry
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the first request warms up the model, subsequent requests are fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduuin05lqng5o64ual13.png" alt="Swagger UI" width="800" height="402"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Production Optimizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend Optimizations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Database Connection Pooling&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;engine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pool_pre_ping&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# Verify connections
&lt;/span&gt;    &lt;span class="n"&gt;pool_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# Connection pool
&lt;/span&gt;    &lt;span class="n"&gt;max_overflow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;        &lt;span class="c1"&gt;# Max additional connections
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Request Timeout Handling&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;  &lt;span class="c1"&gt;# 2 minute timeout
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Logging for Production&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Added proper logging to track issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# In routes
&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generating blog for topic: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generation failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Frontend Optimizations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Environment-based API URL&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_API_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Error Boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Added proper error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;blogAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateBlog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setCurrentBlog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to generate blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Loading States&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clear feedback during long operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isGenerating&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;

    Generating Content...
  &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;

    Generate Blog Post
  &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Cost Breakdown
&lt;/h2&gt;

&lt;p&gt;Let's talk money - one of the best parts!&lt;/p&gt;

&lt;h3&gt;
  
  
  Railway (Backend + Database)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Hobby Plan: $5/month&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Includes backend hosting&lt;/li&gt;
&lt;li&gt;PostgreSQL database (1GB storage)&lt;/li&gt;
&lt;li&gt;Automatic backups&lt;/li&gt;
&lt;li&gt;99.9% uptime SLA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Free Trial:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$5 credit/month for free accounts&lt;/li&gt;
&lt;li&gt;Perfect for side projects and portfolios&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vercel (Frontend)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Hobby Plan: FREE&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unlimited personal projects&lt;/li&gt;
&lt;li&gt;100GB bandwidth/month&lt;/li&gt;
&lt;li&gt;Global CDN&lt;/li&gt;
&lt;li&gt;Automatic HTTPS&lt;/li&gt;
&lt;li&gt;Custom domains&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hugging Face
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Free Tier&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inference API included&lt;/li&gt;
&lt;li&gt;Rate limits apply (but generous)&lt;/li&gt;
&lt;li&gt;Models sleep after inactivity&lt;/li&gt;
&lt;li&gt;Perfect for demos and low-traffic apps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Total Monthly Cost
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;$0 - $5/month&lt;/strong&gt; depending on Railway usage! &lt;/p&gt;

&lt;p&gt;For a production-ready, full-stack AI application, that's incredible value.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI/CD Pipeline: Auto-Deployment
&lt;/h2&gt;

&lt;p&gt;The best part? Both platforms auto-deploy when I push to GitHub!&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make changes&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add new feature"&lt;/span&gt;
git push origin main

&lt;span class="c"&gt;# Automatically:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Railway detects push&lt;/span&gt;
&lt;span class="c"&gt;# 2. Builds backend&lt;/span&gt;
&lt;span class="c"&gt;# 3. Runs tests&lt;/span&gt;
&lt;span class="c"&gt;# 4. Deploys new version&lt;/span&gt;
&lt;span class="c"&gt;# 5. Zero downtime!&lt;/span&gt;

&lt;span class="c"&gt;# Similarly:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Vercel detects push&lt;/span&gt;
&lt;span class="c"&gt;# 2. Builds frontend&lt;/span&gt;
&lt;span class="c"&gt;# 3. Deploys to CDN&lt;/span&gt;
&lt;span class="c"&gt;# 4. Instantly live!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deployment time:&lt;/strong&gt; 2-3 minutes for both!&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment Logs
&lt;/h3&gt;

&lt;p&gt;Railway and Vercel both provide detailed logs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Railway logs showed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Building...
├─ Installing dependencies
├─ Building application  
└─ Starting server

✓ Deployment successful
✓ Health check passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vercel logs showed:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Building...
├─ Installing dependencies
├─ Running build
├─ Optimizing assets
└─ Uploading to CDN

✓ Build completed
✓ Deployment live
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Monitoring &amp;amp; Maintenance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Railway Monitoring
&lt;/h3&gt;

&lt;p&gt;Railway dashboard shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU Usage:&lt;/strong&gt; ~5-10% average&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; ~150MB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Times:&lt;/strong&gt; 50-100ms (excluding AI)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uptime:&lt;/strong&gt; 99.9%&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Vercel Analytics
&lt;/h3&gt;

&lt;p&gt;Enabled Vercel Analytics (free!) to track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Page views&lt;/li&gt;
&lt;li&gt;User locations&lt;/li&gt;
&lt;li&gt;Performance metrics&lt;/li&gt;
&lt;li&gt;Core Web Vitals&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Database Monitoring
&lt;/h3&gt;

&lt;p&gt;Checked PostgreSQL usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; ~5MB (100 blog posts)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connections:&lt;/strong&gt; 2-3 active&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queries:&lt;/strong&gt; Fast (&amp;lt;100ms)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything running smoothly! &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9yppnl4rj4xcvoc2leb.png" alt="Dashboard" width="800" height="615"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Here are the key takeaways from this deployment journey:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Start with Simple Deployment&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Don't overcomplicate. Railway and Vercel handle 90% of DevOps for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Environment Variables Are Critical&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Spent hours debugging before realizing a typo in &lt;code&gt;VITE_API_URL&lt;/code&gt;. Triple-check these!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;CORS Will Get You&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Always configure CORS properly. Test with actual frontend URL, not localhost.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Database Initialization Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In production, tables won't magically appear. Handle database setup in application lifecycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Logs Are Your Best Friend&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Both Railway and Vercel have excellent logging. Use them to debug issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Test Production Early&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Deploy early, deploy often. Catching issues in production is better than finding them in interviews!&lt;/p&gt;

&lt;h3&gt;
  
  
  7. &lt;strong&gt;Free Tiers Are Powerful&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can build and deploy production apps for $0-5/month. No excuses!&lt;/p&gt;




&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;Now that we're in production, here's what I'm planning:&lt;/p&gt;

&lt;h3&gt;
  
  
  Immediate (This Week)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Add rate limiting to prevent abuse&lt;/li&gt;
&lt;li&gt;[ ] Set up error monitoring (Sentry)&lt;/li&gt;
&lt;li&gt;[ ] Add database backup automation&lt;/li&gt;
&lt;li&gt;[ ] Create health check monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Short-term (This Month)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Add user authentication&lt;/li&gt;
&lt;li&gt;[ ] Implement blog favorites&lt;/li&gt;
&lt;li&gt;[ ] Add export to PDF&lt;/li&gt;
&lt;li&gt;[ ] Create API documentation site&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Long-term (This Quarter)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Multi-language support&lt;/li&gt;
&lt;li&gt;[ ] Image generation for blogs&lt;/li&gt;
&lt;li&gt;[ ] Content scheduling&lt;/li&gt;
&lt;li&gt;[ ] Analytics dashboard&lt;/li&gt;
&lt;li&gt;[ ] WordPress integration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Deploying this project taught me more than months of tutorials ever could. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What worked well:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Railway's automatic PostgreSQL setup&lt;/li&gt;
&lt;li&gt;Vercel's instant deployments&lt;/li&gt;
&lt;li&gt;GitHub integration for CI/CD&lt;/li&gt;
&lt;li&gt;Simple, clean architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I'd do differently:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up monitoring from day one&lt;/li&gt;
&lt;li&gt;Add comprehensive error logging earlier&lt;/li&gt;
&lt;li&gt;Test with production environment variables locally&lt;/li&gt;
&lt;li&gt;Document deployment steps as I go&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Most importantly:&lt;/strong&gt; I now have a live, working application in my portfolio. Something I can show in interviews, share with friends, and actually use!&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Tools and platforms used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://railway.app" rel="noopener noreferrer"&gt;Railway&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://vercel.com" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://huggingface.co" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://fastapi.tiangolo.com" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt;&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://react.dev" rel="noopener noreferrer"&gt;React&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Documentation that helped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.railway.app" rel="noopener noreferrer"&gt;Railway Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vercel.com/docs" rel="noopener noreferrer"&gt;Vercel Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastapi.tiangolo.com/deployment/" rel="noopener noreferrer"&gt;FastAPI Deployment&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Yourself!
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://frontend-ochre-rho-71.vercel.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Lymah123/ai-blog-generator" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete code is open source! Feel free to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star the repository&lt;/li&gt;
&lt;li&gt;Fork and customize&lt;/li&gt;
&lt;li&gt;Report issues&lt;/li&gt;
&lt;li&gt;Submit pull requests&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This project reinforced that deployment is where architectural decisions are validated—and where many “working” projects quietly fail&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🙏 Thank You!
&lt;/h2&gt;

&lt;p&gt;Thanks for following along on this journey! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://dev.to/lymah/building-an-ai-blog-generator-with-fastapi-react-and-hugging-face-49m5"&gt;Part 1&lt;/a&gt;&lt;/strong&gt; covered the development process.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Part 2&lt;/strong&gt; (this post) covered deployment.&lt;/p&gt;

&lt;p&gt;What did you learn? What would you do differently? Drop a comment below!&lt;/p&gt;

&lt;p&gt;If you found this helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like this post&lt;/li&gt;
&lt;li&gt;💬 Leave a comment&lt;/li&gt;
&lt;li&gt;🔄 Share with others&lt;/li&gt;
&lt;li&gt;👀 Follow me for more content&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Connect With Me
&lt;/h2&gt;

&lt;p&gt;I'd love to hear your thoughts and answer questions!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://github.com/Lymah123" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://twitter.com/CodesLymah" rel="noopener noreferrer"&gt;Twitter(X)&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.linkedin.com/in/harlimat-odunola-2ab261235" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm already working on v2.0 with exciting features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔐 User authentication&lt;/li&gt;
&lt;li&gt;🖼️ AI-generated blog images&lt;/li&gt;
&lt;li&gt;📅 Content scheduling&lt;/li&gt;
&lt;li&gt;📊 Analytics dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stay tuned!&lt;/strong&gt; Follow me to get notified when the next update drops.&lt;/p&gt;

</description>
      <category>deployment</category>
      <category>devops</category>
      <category>railway</category>
      <category>vercel</category>
    </item>
  </channel>
</rss>
