<?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: 100 Days of Solana</title>
    <description>The latest articles on DEV Community by 100 Days of Solana (@100daysofsolana).</description>
    <link>https://dev.to/100daysofsolana</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.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F12963%2F62b6ce6d-db04-444b-88d1-4bea32484faa.png</url>
      <title>DEV Community: 100 Days of Solana</title>
      <link>https://dev.to/100daysofsolana</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/100daysofsolana"/>
    <language>en</language>
    <item>
      <title>Understanding Program Derived Addresses: The Solana Address That Has No Private Key</title>
      <dc:creator>Vincent Jande</dc:creator>
      <pubDate>Fri, 19 Jun 2026 12:23:35 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/understanding-program-derived-addresses-the-solana-address-that-has-no-private-key-1mcg</link>
      <guid>https://dev.to/100daysofsolana/understanding-program-derived-addresses-the-solana-address-that-has-no-private-key-1mcg</guid>
      <description>&lt;p&gt;Every Solana program eventually hits the same question: where do I put my data, and how do I find it again later?&lt;/p&gt;

&lt;p&gt;Programs are stateless, so a program's data lives in separate accounts, each at an address. The moment you store something, you owe an answer to a problem databases tend to hide from you: what address does this live at, and how does the program find it again tomorrow? Program Derived Addresses are Solana's answer. The name scares people off, but the idea is mostly "an address you compute instead of remember, that only your program can control."&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem, in code
&lt;/h2&gt;

&lt;p&gt;Say each user gets a counter account. The normal way to make an account is to generate a fresh keypair and store data at its public key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;Keypair&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// counter.publicKey is something random, e.g. 7Hx4...9fT&lt;/span&gt;
&lt;span class="c1"&gt;// create the account at that address, write count = 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works. But the address is random, so nothing connects &lt;em&gt;this user&lt;/em&gt; to &lt;em&gt;that address&lt;/em&gt;. Tomorrow, when the user comes back to increment, how does your program find their counter? You're forced to keep a lookup table somewhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the mapping you now have to store and never lose&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counters&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;9fYL...user1&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;7Hx4...9fT&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;B2k9...user2&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;Qz1p...4dR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...times ten thousand users&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lose that table, lose the data, even though the accounts are right there on chain. You're storing files in a warehouse and writing the shelf number on a sticky note.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: compute the address from what you already know
&lt;/h2&gt;

&lt;p&gt;What if the address were a &lt;em&gt;function&lt;/em&gt; of the user instead of random? Give a function the word &lt;code&gt;"counter"&lt;/code&gt; and the user's public key, and it hands back a fixed address. Same inputs, same address, every time. No table.&lt;/p&gt;

&lt;p&gt;That's a PDA. &lt;strong&gt;PDAs are 32-byte addresses derived deterministically from a program ID and a set of seeds.&lt;/strong&gt; The seeds are the meaningful inputs you pick (here, &lt;code&gt;"counter"&lt;/code&gt; + the user's key). With &lt;code&gt;@solana/web3.js&lt;/code&gt;, the library Anchor's client uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;counterPda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bump&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findProgramAddressSync&lt;/span&gt;&lt;span class="p"&gt;(&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;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;counter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;// a label&lt;/span&gt;
    &lt;span class="nx"&gt;userPublicKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;    &lt;span class="c1"&gt;// the user's pubkey&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;MY_PROGRAM_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// counterPda is the SAME every time for this user. No mapping needed.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mapping table is gone: any time you need a user's counter, you re-derive it, and the address itself encodes who it belongs to. With Anchor you'll often skip the explicit call entirely and let the client derive PDAs from the IDL, but under the hood this is what runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The twist: it has no private key
&lt;/h2&gt;

&lt;p&gt;Normal addresses are public keys sitting on the Ed25519 elliptic curve, and every point on that curve has a matching private key. That private key is what signs transactions. But we just &lt;em&gt;hashed&lt;/em&gt; a counter address into existence without making a keypair, so who holds its private key?&lt;/p&gt;

&lt;p&gt;If the hash happened to land on the curve, some stranger might, which would let outsiders sign for your program's accounts. So Solana guarantees a PDA sits &lt;strong&gt;off the curve&lt;/strong&gt;, meaning no private key exists for it. That is the title: an address with no private key. It is the whole point, because it means only the program that derived it can control it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bump, in one example
&lt;/h2&gt;

&lt;p&gt;A given set of seeds plus the program ID produces a valid off-curve address only about half the time, so the derivation includes one more input: a single byte called the &lt;strong&gt;bump&lt;/strong&gt;. The search starts at 255 and counts down, hashing the seeds, the program ID, and the current bump together on each attempt until the result lands off the curve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hash(seeds + program_id + 255) -&amp;gt; on curve?  try 254
hash(seeds + program_id + 254) -&amp;gt; on curve?  try 253
hash(seeds + program_id + 253) -&amp;gt; off curve! use this.  bump = 253
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bump is part of the hash from the very first attempt at 255, not something added only after a bump-less hash fails. The first value that lands off-curve, the highest one that works, is the &lt;strong&gt;canonical bump&lt;/strong&gt;. &lt;code&gt;findProgramAddressSync&lt;/code&gt; returns it as the second value, the &lt;code&gt;bump&lt;/code&gt; above. You'll want to use the canonical bump rather than any other valid one, because the same seeds can produce &lt;em&gt;different&lt;/em&gt; valid addresses under different bumps, and accepting any of them would let an attacker slip a counterfeit account past your checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Anchor: derivation becomes a constraint
&lt;/h2&gt;

&lt;p&gt;On the program side, Anchor does the derivation and the check for you. Creating the counter (storing the bump for later):&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="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(&lt;/span&gt;
        &lt;span class="nd"&gt;init,&lt;/span&gt;
        &lt;span class="nd"&gt;payer&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;authority,&lt;/span&gt;
        &lt;span class="nd"&gt;space&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nd"&gt;Counter::INIT_SPACE,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"counter"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;authority&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                  &lt;span class="c1"&gt;// Anchor finds the canonical bump&lt;/span&gt;
    &lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&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;pub&lt;/span&gt; &lt;span class="n"&gt;system_program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Then later, validating it on every other instruction:&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="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Increment&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(&lt;/span&gt;
        &lt;span class="nd"&gt;mut,&lt;/span&gt;
        &lt;span class="nd"&gt;seeds&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"counter"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;authority&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;key()&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nd"&gt;as_ref()]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="py"&gt;.bump&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;// reuse the stored bump&lt;/span&gt;
    &lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Counter&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;pub&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Read the &lt;code&gt;seeds&lt;/code&gt; line as a rule: &lt;em&gt;this account's address must derive from &lt;code&gt;"counter"&lt;/code&gt; and this authority's key.&lt;/em&gt; When the instruction runs, Anchor re-derives the address and checks that the passed-in account matches. Pass a different account and it's rejected before your code runs. The address is the proof, so there's no mapping left to tamper with. (Store the bump in your &lt;code&gt;Counter&lt;/code&gt; struct at init: &lt;code&gt;pub bump: u8&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;The difference between the two structs is the whole lifecycle: &lt;code&gt;init&lt;/code&gt; &lt;em&gt;creates&lt;/em&gt; the account at the derived address the first time and lets Anchor search for the canonical bump, while the plain &lt;code&gt;seeds&lt;/code&gt; + &lt;code&gt;bump = counter.bump&lt;/code&gt; form &lt;em&gt;validates&lt;/em&gt; an account that already exists, reusing the stored bump so it doesn't pay to re-search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things that trip people up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Seed mismatch between client and program.&lt;/strong&gt; The seeds, their order, and their byte encoding need to match on both sides. &lt;code&gt;Buffer.from("counter")&lt;/code&gt; in the client has to line up with &lt;code&gt;b"counter"&lt;/code&gt; in the program, in the same position. One reordered or mistyped seed derives a different address, and the constraint rejects it. A lot of "my PDA doesn't match" bugs come down to this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not storing the bump.&lt;/strong&gt; If you don't save the canonical bump at init, every later instruction has to re-derive it by searching, which burns compute for no reason. Store it once in the account and reuse it with &lt;code&gt;bump = counter.bump&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Treating the rejection as a bug.&lt;/strong&gt; When you pass the wrong account and the &lt;code&gt;seeds&lt;/code&gt; constraint throws, that's the security model working, not a failure. The point is that only the correctly derived address gets accepted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deriving without the canonical bump.&lt;/strong&gt; Other bump values can produce valid but &lt;em&gt;different&lt;/em&gt; addresses from the same seeds. Sticking with the canonical one, which is what Anchor's bare &lt;code&gt;bump&lt;/code&gt; and &lt;code&gt;findProgramAddressSync&lt;/code&gt; give you by default, keeps client and program in agreement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The payoff: a program that signs for itself
&lt;/h2&gt;

&lt;p&gt;No private key means no human can sign for a PDA, so how does a program move tokens out of an escrow it owns? The runtime gives the owning program a special power: the program whose ID derived the PDA can sign for it, through &lt;code&gt;invoke_signed&lt;/code&gt; during a cross-program invocation. The program's code becomes the authority, with no key anywhere.&lt;/p&gt;

&lt;p&gt;That's the foundation under most escrows, vaults, and lending pools on Solana: a program holding assets no human can drain, releasing them only by its own logic. We'll go deep on &lt;code&gt;invoke_signed&lt;/code&gt; and CPIs in the next post. For now, hold the shape: the missing private key isn't a weakness, it's what lets a program act as a trustless custodian.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Stateless programs store data in accounts at addresses; remembering random addresses doesn't scale.&lt;/li&gt;
&lt;li&gt;A PDA derives the address from seeds + program ID, so you recompute it instead of storing it.&lt;/li&gt;
&lt;li&gt;It's forced off the Ed25519 curve, so no private key exists and no outsider can sign for it.&lt;/li&gt;
&lt;li&gt;The canonical bump (counting down from 255) is the reproducible address you'll normally use.&lt;/li&gt;
&lt;li&gt;In Anchor, &lt;code&gt;seeds&lt;/code&gt; + &lt;code&gt;bump&lt;/code&gt; constraints derive and validate the account for you.&lt;/li&gt;
&lt;li&gt;The deriving program can sign for its own PDA (&lt;code&gt;invoke_signed&lt;/code&gt;), which is what powers escrows and vaults.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Going further
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://solana.com/docs/core/pda" rel="noopener noreferrer"&gt;Solana PDA docs&lt;/a&gt; cover derivation, the canonical bump, and the off-curve guarantee, with runnable examples on the &lt;a href="https://solana.com/docs/core/pda/pda-derivation" rel="noopener noreferrer"&gt;derivation page&lt;/a&gt; (the "Legacy" tab is the &lt;code&gt;@solana/web3.js&lt;/code&gt; version Anchor's client uses). The &lt;a href="https://www.anchor-lang.com/docs/basics/pda" rel="noopener noreferrer"&gt;Anchor PDA guide&lt;/a&gt; walks through the &lt;code&gt;seeds&lt;/code&gt; and &lt;code&gt;bump&lt;/code&gt; constraints you'll actually write, and the full &lt;a href="https://www.anchor-lang.com/docs/references/account-constraints" rel="noopener noreferrer"&gt;account constraints reference&lt;/a&gt; lists every constraint in one place.&lt;/p&gt;

&lt;p&gt;If you're doing 100 Days of Solana, the next arc puts all of this to work, and the challenges should now read like something you already understand. Not joined yet? It's not too late: &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;mlh.link/solana-100&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Arc 8 Catch-Up: Middleware Inside the Token</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Mon, 15 Jun 2026 15:16:14 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-8-catch-up-middleware-inside-the-token-4fe8</link>
      <guid>https://dev.to/100daysofsolana/arc-8-catch-up-middleware-inside-the-token-4fe8</guid>
      <description>&lt;p&gt;Arc 8 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; was about Token-2022.&lt;/p&gt;

&lt;p&gt;That might sound odd at first, because Token-2022 had already shown up in the previous arcs.&lt;/p&gt;

&lt;p&gt;Arc 6 used token extensions to explore fees, interest, frozen accounts, and revocable credentials. Arc 7 used Token Extensions to build NFTs from the same token primitives.&lt;/p&gt;

&lt;p&gt;Arc 8 made the model explicit.&lt;/p&gt;

&lt;p&gt;The whole arc hangs off one idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Token extensions are like middleware that lives inside the asset.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Web2 developers, that is the shift worth noticing.&lt;/p&gt;

&lt;p&gt;In a normal app, rules usually sit around the asset. You write backend code. You add middleware. You call a payment processor. You run a cron job. You trust every integration to go through the right path.&lt;/p&gt;

&lt;p&gt;With Token-2022, some of those rules can live on the mint itself.&lt;/p&gt;

&lt;p&gt;That changes the shape of the system.&lt;/p&gt;

&lt;p&gt;A token is no longer just a balance that moves between accounts. It can be a balance with transfer fees, interest display, transfer restrictions, or other behavior attached.&lt;/p&gt;

&lt;p&gt;The middleware is not next to the token.&lt;/p&gt;

&lt;p&gt;It is part of the token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token rules should not depend on every app remembering them
&lt;/h2&gt;

&lt;p&gt;Most Web2 developers have built systems where rules sit outside the thing being moved.&lt;/p&gt;

&lt;p&gt;A marketplace might charge a platform fee.&lt;br&gt;&lt;br&gt;
A fintech app might show yield.&lt;br&gt;&lt;br&gt;
A loyalty system might prevent points from being transferred.&lt;br&gt;&lt;br&gt;
A membership product might issue a badge that cannot be sold.  &lt;/p&gt;

&lt;p&gt;Usually, the rule lives somewhere in your application.&lt;/p&gt;

&lt;p&gt;You might write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;calculateFee()
applyInterest()
rejectTransfer()
checkMembershipStatus()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works as long as every relevant path uses the same logic.&lt;/p&gt;

&lt;p&gt;But that is the weak point.&lt;/p&gt;

&lt;p&gt;One backend service might apply the fee. Another script might forget it. An integration partner might call the wrong endpoint. An admin tool might bypass the normal flow. A scheduled job might fail. A frontend might display one thing while the ledger stores another.&lt;/p&gt;

&lt;p&gt;Token-2022 takes a different approach.&lt;/p&gt;

&lt;p&gt;When the extension is configured on the mint, the Token-2022 program enforces the behavior consistently. Every wallet, CLI, dApp, and program that interacts with the token has to deal with the same rule.&lt;/p&gt;

&lt;p&gt;That is why the middleware analogy is useful, but only if you take it one step further.&lt;/p&gt;

&lt;p&gt;This is not middleware sitting in your app stack.&lt;/p&gt;

&lt;p&gt;It is middleware baked into the asset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transfer fees are enforced by the mint
&lt;/h2&gt;

&lt;p&gt;Arc 8 started with a fee-bearing token.&lt;/p&gt;

&lt;p&gt;The exercise was simple: create a mint under the Token-2022 program, add a transfer fee configuration, mint some supply, and inspect the result.&lt;/p&gt;

&lt;p&gt;The important part was not the percentage.&lt;/p&gt;

&lt;p&gt;It was where the rule lived.&lt;/p&gt;

&lt;p&gt;The transfer fee was not a line of backend code. It was not a webhook. It was not a checkout rule. It was not a convention that wallets were expected to follow voluntarily.&lt;/p&gt;

&lt;p&gt;The fee configuration lived on the mint.&lt;/p&gt;

&lt;p&gt;That means the token itself carried the rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When this token moves, apply this fee.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Web2 developers, the contrast is familiar.&lt;/p&gt;

&lt;p&gt;If you charge a fee through Stripe, you usually build fee logic around the payment flow. You decide how much to collect, when to collect it, how to reconcile it, and which integrations are allowed to move value.&lt;/p&gt;

&lt;p&gt;With Token-2022, the fee rule is part of the asset’s behavior. The transfer goes through the token program, and the token program applies the rule.&lt;/p&gt;

&lt;p&gt;That is a meaningful difference.&lt;/p&gt;

&lt;p&gt;You are not trusting every app to remember the fee. You are configuring a token that cannot move without the fee logic being considered.&lt;/p&gt;

&lt;p&gt;That is the first big Arc 8 lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The rule travels with the token.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The fee lifecycle is transfer, withhold, withdraw
&lt;/h2&gt;

&lt;p&gt;Creating a fee-bearing mint is only the first part.&lt;/p&gt;

&lt;p&gt;Arc 8 then put the token in motion and followed the fee lifecycle end to end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;transfer → withhold → withdraw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That lifecycle matters because transfer fees do not behave like a normal payment processor settlement flow.&lt;/p&gt;

&lt;p&gt;When a fee-bearing token is transferred, the recipient does not simply receive the full amount and then send a fee somewhere else.&lt;/p&gt;

&lt;p&gt;Instead, the fee is withheld in the recipient’s token account.&lt;/p&gt;

&lt;p&gt;For example, with a 1% transfer fee, a transfer of 1,000 tokens gives the recipient 990 spendable tokens. The remaining 10 are recorded as withheld tokens on the recipient’s token account.&lt;/p&gt;

&lt;p&gt;Those withheld tokens are not spendable by the recipient. They sit there until the withdraw authority collects them.&lt;/p&gt;

&lt;p&gt;That is a very different mental model from a Web2 marketplace fee.&lt;/p&gt;

&lt;p&gt;In a normal app, you might have a payment ledger, a treasury balance, a settlement job, a reconciliation process, and a dashboard showing fees owed.&lt;/p&gt;

&lt;p&gt;Here, the withheld amount is visible in the token account itself. The fee authority can later withdraw it using the Token-2022 program.&lt;/p&gt;

&lt;p&gt;The important thing is what you did not build.&lt;/p&gt;

&lt;p&gt;No custom program.&lt;br&gt;&lt;br&gt;
No payment processor flow.&lt;br&gt;&lt;br&gt;
No webhook.&lt;br&gt;&lt;br&gt;
No cron job.&lt;br&gt;&lt;br&gt;
No separate fee table.  &lt;/p&gt;

&lt;p&gt;The protocol enforced the fee and stored the withheld amount.&lt;/p&gt;

&lt;p&gt;That is the kind of concrete behavior that makes Token-2022 easier to understand. It is not an abstract extension system. It is a rule you can observe on devnet.&lt;/p&gt;
&lt;h2&gt;
  
  
  Composability means extensions share the same mint
&lt;/h2&gt;

&lt;p&gt;Arc 8 then combined transfer fees with interest-bearing behavior.&lt;/p&gt;

&lt;p&gt;This is where the middleware analogy becomes more powerful.&lt;/p&gt;

&lt;p&gt;A token can have more than one rule.&lt;/p&gt;

&lt;p&gt;The mint can say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Charge a fee when tokens move.
Display balances with interest over time.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those behaviors are different, but they can coexist on the same mint.&lt;/p&gt;

&lt;p&gt;That matters because Web2 developers often expect separate systems for separate behaviors.&lt;/p&gt;

&lt;p&gt;A fee might belong to a payments service.&lt;br&gt;&lt;br&gt;
Interest might belong to a financial calculation service.&lt;br&gt;&lt;br&gt;
Metadata might belong to an asset database.&lt;br&gt;&lt;br&gt;
Restrictions might belong to an access control system.  &lt;/p&gt;

&lt;p&gt;Token-2022 lets those behaviors be configured as extensions on one mint.&lt;/p&gt;

&lt;p&gt;The technical reason is that extensions are stored as structured data on the account. The mint has enough space allocated for the extensions, and the Token-2022 program knows how to read and enforce them.&lt;/p&gt;

&lt;p&gt;But the product lesson is simpler:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A token can be configured with multiple behaviors at once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The fee and interest example also made an important distinction clear.&lt;/p&gt;

&lt;p&gt;The transfer fee works on the raw token amount. It moves real token units and withholds part of the transfer.&lt;/p&gt;

&lt;p&gt;Interest-bearing behavior affects the displayed amount. It does not continuously mint new tokens in the background. The raw balance does not change every second. The UI amount is computed from the stored amount, rate, and elapsed time.&lt;/p&gt;

&lt;p&gt;That is why the two features can coexist cleanly.&lt;/p&gt;

&lt;p&gt;The fee changes what happens during transfer.&lt;br&gt;&lt;br&gt;
The interest extension changes how the balance is interpreted when read.  &lt;/p&gt;

&lt;p&gt;Those are different layers of behavior on the same asset.&lt;/p&gt;
&lt;h2&gt;
  
  
  Interest is a view, not a background job
&lt;/h2&gt;

&lt;p&gt;The interest-bearing extension is one of the easiest places for Web2 instincts to mislead you.&lt;/p&gt;

&lt;p&gt;In a conventional app, if a balance grows, you usually assume something wrote a new value somewhere.&lt;/p&gt;

&lt;p&gt;A scheduled job updated the balance.&lt;br&gt;&lt;br&gt;
A database row changed.&lt;br&gt;&lt;br&gt;
A ledger entry was added.&lt;br&gt;&lt;br&gt;
A batch process applied interest overnight.  &lt;/p&gt;

&lt;p&gt;Token-2022 does not need to work that way.&lt;/p&gt;

&lt;p&gt;With the interest-bearing extension, the raw token amount stays the same unless an actual token instruction changes it. What changes is the displayed amount.&lt;/p&gt;

&lt;p&gt;The program can calculate the UI amount based on the interest rate and time elapsed.&lt;/p&gt;

&lt;p&gt;That is why Arc 8 deliberately used a high rate on devnet. At realistic rates, the change over a short challenge window would be too small to notice. A dramatic rate makes the model visible.&lt;/p&gt;

&lt;p&gt;The lesson is not “high yield tokens are good.”&lt;/p&gt;

&lt;p&gt;The lesson is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Display can be computed from state without constantly rewriting state.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Web2 developers, that is a useful mental shift.&lt;/p&gt;

&lt;p&gt;The token account stores raw units. The extension changes how those units are displayed. If you assume “displayed balance increased” means “new tokens were minted,” you will misunderstand what the program is doing.&lt;/p&gt;

&lt;p&gt;Arc 8 forced that distinction into the open.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reading mint configuration is part of building
&lt;/h2&gt;

&lt;p&gt;One of the most useful Arc 8 exercises was not creating another mint.&lt;/p&gt;

&lt;p&gt;It was auditing the mints already created.&lt;/p&gt;

&lt;p&gt;That matters because Token-2022 configuration is public state. You can inspect a mint and see which extensions it uses. You can read the fee configuration. You can see whether interest-bearing behavior exists. You can check whether a token is non-transferable.&lt;/p&gt;

&lt;p&gt;That is the Solana version of inspecting a production schema.&lt;/p&gt;

&lt;p&gt;In Web2, the rules of a system are often scattered across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;application code&lt;/li&gt;
&lt;li&gt;database migrations&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;payment processor settings&lt;/li&gt;
&lt;li&gt;admin dashboards&lt;/li&gt;
&lt;li&gt;scheduled jobs&lt;/li&gt;
&lt;li&gt;private documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Solana, much of the token’s behavior is visible in the mint account.&lt;/p&gt;

&lt;p&gt;That does not mean the whole application is transparent. But it does mean the token’s configuration can be read by anyone.&lt;/p&gt;

&lt;p&gt;That is part of the Token-2022 pitch.&lt;/p&gt;

&lt;p&gt;The behavior is public.&lt;br&gt;&lt;br&gt;
The configuration is verifiable.&lt;br&gt;&lt;br&gt;
The rules cannot be silently swapped inside a private backend.  &lt;/p&gt;

&lt;p&gt;That is also why auditing matters.&lt;/p&gt;

&lt;p&gt;You should not just create a mint and trust that you typed the command correctly. You should read it back. Check the extensions. Check the authorities. Check the raw configuration. Explain what each extension does in plain English.&lt;/p&gt;

&lt;p&gt;That last part is important.&lt;/p&gt;

&lt;p&gt;If you cannot describe the behavior without looking it up, you probably do not understand the asset yet.&lt;/p&gt;
&lt;h2&gt;
  
  
  Non-transferable tokens are useful because they fail
&lt;/h2&gt;

&lt;p&gt;Arc 8 ended the build sequence with a token that refuses to move.&lt;/p&gt;

&lt;p&gt;That might sound strange because tokens usually imply transferability. Money moves. Points move. Assets move.&lt;/p&gt;

&lt;p&gt;But not every token-like thing should be transferable.&lt;/p&gt;

&lt;p&gt;A course certificate should not be sellable.&lt;br&gt;&lt;br&gt;
A membership badge might need to stay with the person who earned it.&lt;br&gt;&lt;br&gt;
A compliance credential might need to remain attached to one wallet.&lt;br&gt;&lt;br&gt;
A proof-of-attendance token might lose meaning if it can be traded.  &lt;/p&gt;

&lt;p&gt;The non-transferable extension lets the mint encode that rule.&lt;/p&gt;

&lt;p&gt;The challenge deliberately tried to break it. Create the token. Mint it. Create a recipient account. Attempt a transfer.&lt;/p&gt;

&lt;p&gt;The transfer fails.&lt;/p&gt;

&lt;p&gt;That failure is the feature.&lt;/p&gt;

&lt;p&gt;In a Web2 app, you might prevent transferability through an API check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if token.non_transferable:
    reject transfer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that depends on every path using the same API.&lt;/p&gt;

&lt;p&gt;With Token-2022, the token program itself rejects the transfer. The rule is part of the mint’s behavior, and any client interacting with the program has to respect it.&lt;/p&gt;

&lt;p&gt;That makes non-transferable tokens different from fees and interest.&lt;/p&gt;

&lt;p&gt;Fees and interest are still money-like features. They affect value movement and balance display.&lt;/p&gt;

&lt;p&gt;Non-transferability changes the category of the asset. The token starts to look less like currency and more like identity, membership, status, or proof.&lt;/p&gt;

&lt;p&gt;Same mint model. Same token accounts. Same CLI.&lt;/p&gt;

&lt;p&gt;Different product meaning.&lt;/p&gt;

&lt;p&gt;That is why the failed transfer matters so much. It proves the extension is not just descriptive metadata. It changes what the token can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token-2022 is configuration, but not casual configuration
&lt;/h2&gt;

&lt;p&gt;A tempting takeaway from Arc 8 would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token-2022 lets you add features with flags.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is true, but incomplete.&lt;/p&gt;

&lt;p&gt;The better takeaway is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Token-2022 lets you design token behavior through mint configuration.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;Extensions feel easy because they can be created from the CLI. But they are not throwaway settings. They shape what the asset is, who can use it, and how every integration will experience it.&lt;/p&gt;

&lt;p&gt;A fee-bearing token is different from a normal token.&lt;/p&gt;

&lt;p&gt;An interest-bearing token is different from a normal token.&lt;/p&gt;

&lt;p&gt;A non-transferable token is very different from a normal token.&lt;/p&gt;

&lt;p&gt;Once those behaviors are on the mint, they are part of the asset’s design.&lt;/p&gt;

&lt;p&gt;That brings Arc 8 back to the product questions underneath the technical work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should every transfer charge a fee?
Who can withdraw withheld fees?
Should balances display with interest?
Should this asset be transferable at all?
Which authorities control the rules?
Will wallets and integrations understand these extensions?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are not just CLI questions.&lt;/p&gt;

&lt;p&gt;They are product, governance, and integration questions.&lt;/p&gt;

&lt;p&gt;Token-2022 gives you reusable building blocks, but you still have to choose the right ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing turns extensions into patterns
&lt;/h2&gt;

&lt;p&gt;Arc 8 ended by turning the week into a public post and social thread.&lt;/p&gt;

&lt;p&gt;That fits the arc well because Token-2022 is easy to explain badly.&lt;/p&gt;

&lt;p&gt;You can list extensions all day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transfer fees&lt;/li&gt;
&lt;li&gt;interest-bearing&lt;/li&gt;
&lt;li&gt;non-transferable&lt;/li&gt;
&lt;li&gt;default account state&lt;/li&gt;
&lt;li&gt;permanent delegate&lt;/li&gt;
&lt;li&gt;confidential transfers&lt;/li&gt;
&lt;li&gt;memo transfer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But a list is not a mental model.&lt;/p&gt;

&lt;p&gt;A useful post needs to show the pattern.&lt;/p&gt;

&lt;p&gt;For Arc 8, the pattern was a trilogy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A token that charges a fee.
A token that charges a fee and displays interest.
A token that refuses to move.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is much more memorable than “I tried some Token-2022 extensions.”&lt;/p&gt;

&lt;p&gt;It shows the range of what extensions can do.&lt;/p&gt;

&lt;p&gt;One changes transfer economics.&lt;br&gt;&lt;br&gt;
One composes transfer economics with display behavior.&lt;br&gt;&lt;br&gt;
One changes whether transfer is allowed at all.  &lt;/p&gt;

&lt;p&gt;That is the story.&lt;/p&gt;

&lt;p&gt;The best write-up would include what actually happened on devnet: the fee being withheld, the interest-adjusted UI amount changing without a transaction, and the failed transfer error from the non-transferable token.&lt;/p&gt;

&lt;p&gt;Those details matter because they prove the learning.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;p&gt;“I learned Token-2022.”&lt;/p&gt;

&lt;p&gt;More like:&lt;/p&gt;

&lt;p&gt;“I created a token that charged a transfer fee, watched the fee sit as a withheld amount on the recipient account, stacked interest on the same mint, then built a token that refused to transfer at all.”&lt;/p&gt;

&lt;p&gt;That is useful developer storytelling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 8 sets up
&lt;/h2&gt;

&lt;p&gt;Strip Arc 8 back to its core and the main ideas are clear:&lt;/p&gt;

&lt;p&gt;Token-2022 lets token behavior live on the mint. Transfer fees are enforced by the token program. Fees follow a visible lifecycle: transfer, withhold, withdraw. Interest-bearing behavior changes displayed amounts without constantly rewriting raw balances. Extensions can compose on the same mint. Mint configuration is public and inspectable. Non-transferable tokens show that extensions are not only about money; they can turn tokens into identity, credential, or membership objects.&lt;/p&gt;

&lt;p&gt;That is the real shift.&lt;/p&gt;

&lt;p&gt;Arc 5 taught us to create and manage tokens.&lt;/p&gt;

&lt;p&gt;Arc 6 taught us to design token behavior with extensions.&lt;/p&gt;

&lt;p&gt;Arc 7 showed that NFTs are built from the same token primitives.&lt;/p&gt;

&lt;p&gt;Arc 8 pulled the model together: Token-2022 is the extension system that makes many of those behaviors possible without custom on-chain programs.&lt;/p&gt;

&lt;p&gt;From here, the question becomes more practical:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do you choose the right token behavior for the product you are building?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the mindset to carry forward.&lt;/p&gt;

&lt;p&gt;Use this post as the map, revisit the Arc 8 challenges when you want the hands-on version, and remember the central lesson: with Token-2022, the rule does not sit beside the asset. The rule can live inside the asset.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>learning</category>
    </item>
    <item>
      <title>Arc 7 Catch-Up: Building NFTs from First Principles</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Mon, 15 Jun 2026 14:28:59 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-7-catch-up-building-nfts-from-first-principles-5ae3</link>
      <guid>https://dev.to/100daysofsolana/arc-7-catch-up-building-nfts-from-first-principles-5ae3</guid>
      <description>&lt;p&gt;Arc 7 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; was about building NFTs from first principles.&lt;/p&gt;

&lt;p&gt;Instead of starting with marketplaces, profile pictures, or NFT culture, the arc stripped the model back to the underlying token mechanics: supply, decimals, mint authority, metadata, collections, and provenance.&lt;/p&gt;

&lt;p&gt;After Arc 6, we already knew that Token-2022 can turn tokens into programmable objects with rules: interest, fees, frozen accounts, credentials, revocation, and metadata.&lt;/p&gt;

&lt;p&gt;Arc 7 took that model into NFTs.&lt;/p&gt;

&lt;p&gt;But the useful surprise was that NFTs are not a completely separate world.&lt;/p&gt;

&lt;p&gt;The whole arc hangs off one idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A Solana NFT is built from the same token primitives you already know.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Web2 developers, that is the shift worth noticing.&lt;/p&gt;

&lt;p&gt;An NFT is not magic. It is not “a JPEG on the blockchain.” It is not a special category of database record maintained by some mysterious NFT system.&lt;/p&gt;

&lt;p&gt;At its simplest, an NFT is a token mint with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;supply locked at one&lt;/li&gt;
&lt;li&gt;zero decimals&lt;/li&gt;
&lt;li&gt;mint authority disabled&lt;/li&gt;
&lt;li&gt;metadata attached&lt;/li&gt;
&lt;li&gt;optional collection membership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the foundation Arc 7 built up, one layer at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  NFTs are not separate from tokens
&lt;/h2&gt;

&lt;p&gt;Arc 7 started by stripping the NFT model down to its smallest useful form.&lt;/p&gt;

&lt;p&gt;Before metadata, images, attributes, marketplaces, or collections, an NFT is just a token that cannot be split and cannot be duplicated.&lt;/p&gt;

&lt;p&gt;That means two choices matter immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;decimals = 0
supply = 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero decimals means the token cannot be divided into fractions. You cannot own 0.5 of it.&lt;/p&gt;

&lt;p&gt;Supply of one means there is only one unit.&lt;/p&gt;

&lt;p&gt;But supply of one is not enough by itself. If the mint authority still exists, someone could mint another unit later.&lt;/p&gt;

&lt;p&gt;So the final step is to disable the mint authority.&lt;/p&gt;

&lt;p&gt;That is the moment the token becomes meaningfully non-fungible. The network can see that the supply is one, the decimals are zero, and no authority remains that can create a second copy.&lt;/p&gt;

&lt;p&gt;For Web2 developers, a useful comparison is a unique database row with a primary key.&lt;/p&gt;

&lt;p&gt;But the analogy only goes so far.&lt;/p&gt;

&lt;p&gt;In a normal database, the application owner can usually edit the row, clone it, delete it, or change the surrounding rules. On Solana, once the mint authority is disabled, the supply constraint is enforced by the token program.&lt;/p&gt;

&lt;p&gt;The uniqueness is not a UI convention. It is not a marketplace promise. It is part of the token state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata turns a token into something recognizable
&lt;/h2&gt;

&lt;p&gt;The first NFT in Arc 7 was intentionally plain.&lt;/p&gt;

&lt;p&gt;It had no name, no image, no creator story, no attributes, and no collection. It was non-fungible, but not especially meaningful to a human.&lt;/p&gt;

&lt;p&gt;That is where metadata comes in.&lt;/p&gt;

&lt;p&gt;Arc 7 used Token-2022 metadata to give the NFT a name, symbol, and URI. The URI pointed to an off-chain JSON file containing richer information such as the description, image, and attributes.&lt;/p&gt;

&lt;p&gt;That gives us a two-layer model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On-chain:
mint, supply, decimals, authority, name, symbol, URI

Off-chain:
image, description, attributes, richer display data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That split matters.&lt;/p&gt;

&lt;p&gt;The expensive, consensus-critical parts live on-chain. The heavier display information usually lives off-chain. Wallets and explorers follow the pointer: read the mint, get the URI, fetch the JSON, render the asset.&lt;/p&gt;

&lt;p&gt;For Web2 developers, this is familiar once you stop treating the NFT as mysterious.&lt;/p&gt;

&lt;p&gt;The on-chain metadata is like the core record. The URI is like a foreign key or external reference. The JSON is like the richer document the UI uses to render the page.&lt;/p&gt;

&lt;p&gt;The important difference is that the ownership, supply, and core metadata live on a shared network rather than inside one application database.&lt;/p&gt;

&lt;p&gt;Arc 7 also made an important historical point. In older Solana NFT workflows, metadata often lived in a separate Metaplex Token Metadata account next to the mint. In this arc, Token Extensions put metadata directly on the mint account itself.&lt;/p&gt;

&lt;p&gt;That is why the exercise was useful. It showed the foundation before introducing convenience tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  An NFT collection is an on-chain relationship
&lt;/h2&gt;

&lt;p&gt;Once the arc had created a single NFT, the next step was collections.&lt;/p&gt;

&lt;p&gt;In Web2 terms, this is easy to understand.&lt;/p&gt;

&lt;p&gt;A product belongs to a catalog.&lt;br&gt;
A ticket belongs to an event.&lt;br&gt;
A badge belongs to an award program.&lt;br&gt;
A collectible belongs to a set.&lt;/p&gt;

&lt;p&gt;In a database, you might model that with a foreign key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nft.collection_id = collection.id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arc 7 built the Solana equivalent using Token-2022 group and member extensions.&lt;/p&gt;

&lt;p&gt;Strictly speaking, there are four related pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GroupPointer&lt;/code&gt;, which points a collection mint at the account that stores group data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TokenGroup&lt;/code&gt;, which stores the group data itself&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GroupMemberPointer&lt;/code&gt;, which points an NFT mint at the account that stores member data&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TokenGroupMember&lt;/code&gt;, which stores the member’s group address and member number&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the arc, those pointers point back to the mint accounts themselves, so the group and member data live directly on the relevant mints.&lt;/p&gt;

&lt;p&gt;That is why it is fair to talk about a collection mint and member mints, but the underlying model is a little more precise than “one group extension and one member extension.”&lt;/p&gt;

&lt;p&gt;The collection mint represents the group. Each member NFT stores membership data that links it to that group.&lt;/p&gt;

&lt;p&gt;The important part is that this relationship is not just a label in a marketplace database. It is data stored on-chain and readable by wallets, explorers, and programs.&lt;/p&gt;

&lt;p&gt;The arc’s useful mental model was simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;collection mint = the group
member mint = the individual NFT
member data = the link back to the group
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the NFT version of a foreign key relationship.&lt;/p&gt;

&lt;p&gt;But there is an extra trust step. A pointer by itself is not proof. A malicious mint can point at something it should not. So a client should not stop at “this member says it belongs to this collection.”&lt;/p&gt;

&lt;p&gt;It needs to resolve the pointer and verify that the relationship is internally consistent.&lt;/p&gt;

&lt;p&gt;That means checking that the member identifies the collection, and that the account being pointed to actually identifies the mint back.&lt;/p&gt;

&lt;p&gt;And the creation step matters too. Initializing a group member is not just something any random token creator can do unilaterally. The collection’s authority has to authorize membership.&lt;/p&gt;

&lt;p&gt;That is the stronger provenance claim: the collection relationship is not just stored on-chain; it is created through an authorized on-chain action.&lt;/p&gt;

&lt;p&gt;As with Arc 6, the schema decisions matter. A mint must be created with the right extensions. You cannot casually retrofit yesterday’s NFT into a collection if it was not created with the member structure it needs.&lt;/p&gt;

&lt;p&gt;That constraint can feel annoying, but it is also part of what makes the structure verifiable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provenance is something you can inspect
&lt;/h2&gt;

&lt;p&gt;One of the most valuable Arc 7 exercises was not creating another asset.&lt;/p&gt;

&lt;p&gt;It was auditing the collection.&lt;/p&gt;

&lt;p&gt;That matters because NFT language often gets vague. Provenance, authenticity, ownership, rarity, official collections — these terms can become marketing fog very quickly.&lt;/p&gt;

&lt;p&gt;Arc 7 made the idea concrete.&lt;/p&gt;

&lt;p&gt;You inspect the collection mint.&lt;br&gt;
You inspect the member NFT.&lt;br&gt;
You check the member data.&lt;br&gt;
You verify that the collection relationship resolves correctly.&lt;/p&gt;

&lt;p&gt;That last step matters.&lt;/p&gt;

&lt;p&gt;A one-way check is not enough. It is not sufficient to say:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This NFT points to collection C.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safer check is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This NFT points to member data.
That member data names this NFT mint.
That member data names collection C.
The group data for collection C names collection C.
The membership was created under the collection’s authority.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is what makes the relationship more than vibes.&lt;/p&gt;

&lt;p&gt;The collection claim is not true merely because a website says so. It is not true merely because a field contains a familiar collection address. It is true when the on-chain relationship resolves correctly and the collection authority has authorized that membership.&lt;/p&gt;

&lt;p&gt;For Web2 developers, this is like checking that a foreign key resolves correctly and that the row was created by a service with permission to write to that table.&lt;/p&gt;

&lt;p&gt;The difference is that the database is public, shared, and inspectable by anyone.&lt;/p&gt;

&lt;p&gt;That is a big part of the NFT value proposition when you remove the hype.&lt;/p&gt;

&lt;p&gt;The asset can carry enough information for independent verification, but only if clients verify the relationship properly. Pointers are useful because they let accounts reference each other. They are dangerous if treated as proof on their own.&lt;/p&gt;

&lt;p&gt;That is also why the CLI work matters.&lt;/p&gt;

&lt;p&gt;It is tempting to think of NFTs through wallets, marketplaces, and images. But the more useful developer habit is to read the underlying state.&lt;/p&gt;

&lt;p&gt;What is the supply?&lt;br&gt;
What are the decimals?&lt;br&gt;
Is the mint authority disabled?&lt;br&gt;
Is metadata present?&lt;br&gt;
What URI is stored?&lt;br&gt;
Is there group member data?&lt;br&gt;
Does it name the expected collection?&lt;br&gt;
Does the pointed-to data name this mint back?&lt;br&gt;
Was the membership authorized by the collection authority?&lt;/p&gt;

&lt;p&gt;That is how NFTs stop feeling like magic.&lt;/p&gt;
&lt;h2&gt;
  
  
  Mutability is a design choice
&lt;/h2&gt;

&lt;p&gt;Arc 7 then moved from creation to mutation.&lt;/p&gt;

&lt;p&gt;Because the creator still held the metadata update authority, they could rename the NFT, add a custom field, remove that field, or point the URI at a new JSON file.&lt;/p&gt;

&lt;p&gt;That is an important lesson because “on-chain” does not always mean “unchangeable.”&lt;/p&gt;

&lt;p&gt;Some parts of the NFT were locked. The supply was one. The decimals were zero. The mint authority had been disabled.&lt;/p&gt;

&lt;p&gt;But the metadata could still change while the update authority existed.&lt;/p&gt;

&lt;p&gt;That is not automatically good or bad. It is a design choice.&lt;/p&gt;

&lt;p&gt;A game asset might need metadata updates as it levels up.&lt;br&gt;
A ticket might need status changes.&lt;br&gt;
A credential might need expiry data.&lt;br&gt;
A dynamic collectible might intentionally evolve over time.&lt;/p&gt;

&lt;p&gt;But for other assets, mutability might undermine trust.&lt;/p&gt;

&lt;p&gt;If someone buys an NFT because of its image, description, or attributes, they care about whether those things can change later. If a collection promises permanence, the update authority becomes part of the trust model.&lt;/p&gt;

&lt;p&gt;Arc 7 also showed a practical split between on-chain and off-chain updates.&lt;/p&gt;

&lt;p&gt;Changing the on-chain name or URI can happen quickly. Changing the image behind the URI depends on off-chain hosting and caching. Wallets and explorers may continue showing an old image for a while, even after the metadata has changed.&lt;/p&gt;

&lt;p&gt;That is a useful reminder: NFTs are not purely on-chain objects.&lt;/p&gt;

&lt;p&gt;They are hybrids.&lt;/p&gt;

&lt;p&gt;The token, ownership, supply, and metadata pointer can live on-chain. The image and richer media often live somewhere else.&lt;/p&gt;

&lt;p&gt;So the storage decision matters. A temporary GitHub Gist is fine for devnet learning. A serious project needs to think harder about durable storage: Arweave is designed for pay-once permanent storage, while IPFS content remains available only while someone continues to pin or serve it.&lt;/p&gt;
&lt;h2&gt;
  
  
  NFTs are records with ownership, display, and history
&lt;/h2&gt;

&lt;p&gt;The practical Web2 bridge for Arc 7 was not “NFTs are images.”&lt;/p&gt;

&lt;p&gt;A better bridge is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NFTs are unique records with ownership, display metadata, and provenance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That framing is much more useful.&lt;/p&gt;

&lt;p&gt;A normal application might have unique records everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;event tickets&lt;/li&gt;
&lt;li&gt;certificates&lt;/li&gt;
&lt;li&gt;software licenses&lt;/li&gt;
&lt;li&gt;game items&lt;/li&gt;
&lt;li&gt;collectible cards&lt;/li&gt;
&lt;li&gt;access passes&lt;/li&gt;
&lt;li&gt;membership badges&lt;/li&gt;
&lt;li&gt;digital art editions&lt;/li&gt;
&lt;li&gt;proof-of-attendance records&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Web2, those records usually live inside one company’s database. Ownership and transfer rules are whatever that application says they are.&lt;/p&gt;

&lt;p&gt;On Solana, the unique record can be represented as a token mint. Ownership can be held by a wallet. Supply can be locked. Metadata can be attached. Collection membership can be verified.&lt;/p&gt;

&lt;p&gt;That does not mean every unique record should be an NFT.&lt;/p&gt;

&lt;p&gt;It does mean the NFT model is easier to reason about once you stop starting with the JPEG.&lt;/p&gt;

&lt;p&gt;Start with the record.&lt;/p&gt;

&lt;p&gt;Then ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should this record be unique?
Should ownership be visible?
Should it be transferable?
Should it belong to a collection?
Should its metadata be mutable?
Should the media be permanent?
Who should hold the update authority?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are product questions, not just blockchain questions.&lt;/p&gt;

&lt;p&gt;That is the thread connecting Arc 7 back to Arc 6.&lt;/p&gt;

&lt;p&gt;Token design is product design. NFT design is product design too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing forces the model to become simple
&lt;/h2&gt;

&lt;p&gt;Arc 7 ended, like the earlier arcs, by writing and sharing.&lt;/p&gt;

&lt;p&gt;That matters because NFTs are surrounded by baggage. People bring assumptions from marketplaces, profile pictures, speculation, scams, and culture wars.&lt;/p&gt;

&lt;p&gt;A useful developer write-up has to cut through that.&lt;/p&gt;

&lt;p&gt;The best post from this arc would not start with “NFTs are back” or “NFTs are dead.” It would explain what was actually built:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I created a token with supply 1 and zero decimals.
I disabled the mint authority.
I added metadata.
I pointed the metadata at a JSON file.
I created a collection mint.
I linked member NFTs to the collection.
I inspected the on-chain relationship.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is enough.&lt;/p&gt;

&lt;p&gt;The point is not to defend NFTs as a category. The point is to understand the technical model.&lt;/p&gt;

&lt;p&gt;A good write-up might focus on one concrete surprise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an NFT is just a token mint with specific constraints&lt;/li&gt;
&lt;li&gt;metadata can live directly on the mint with Token Extensions&lt;/li&gt;
&lt;li&gt;collection membership can be verified on-chain&lt;/li&gt;
&lt;li&gt;provenance is an authorized relationship, not just a marketplace claim&lt;/li&gt;
&lt;li&gt;metadata can be mutable if the update authority remains active&lt;/li&gt;
&lt;li&gt;off-chain media is only as durable as the place it is hosted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That kind of explanation is useful because it gives the next Web2 developer a handle on the system.&lt;/p&gt;

&lt;p&gt;Not hype.&lt;/p&gt;

&lt;p&gt;A model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 7 sets up
&lt;/h2&gt;

&lt;p&gt;Strip Arc 7 back to its core and the main ideas are clear:&lt;/p&gt;

&lt;p&gt;A Solana NFT is built from token primitives. Non-fungibility comes from zero decimals, supply of one, and disabled mint authority. Metadata makes the asset recognizable. Off-chain JSON gives wallets and explorers richer display information. Collections are on-chain relationships between group and member data. Provenance is stronger than a pointer: the member relationship must be authorized by the collection authority and verified in both directions so pointer spoofing does not masquerade as legitimacy. Metadata mutability depends on who controls the update authority.&lt;/p&gt;

&lt;p&gt;That is the real shift.&lt;/p&gt;

&lt;p&gt;Arc 5 taught us to create and manage tokens.&lt;/p&gt;

&lt;p&gt;Arc 6 taught us to design token behavior with extensions.&lt;/p&gt;

&lt;p&gt;Arc 7 showed that NFTs are not a separate technology stack. They are the same token model, shaped into unique digital assets with metadata and provenance.&lt;/p&gt;

&lt;p&gt;From here, the question becomes more practical:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do these assets interact with real programs, wallets, apps, and users?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where the next arc can take the model beyond minting and inspection, and into more realistic application behavior.&lt;/p&gt;

&lt;p&gt;Use this post as the map, revisit the Arc 7 challenges when you want the hands-on version, and carry the NFT mental model into what comes next.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>learning</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>Arc 6 Catch-Up: Designing Tokens That Enforce Rules</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Mon, 15 Jun 2026 13:46:26 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-6-catch-up-designing-tokens-that-enforce-rules-3ffk</link>
      <guid>https://dev.to/100daysofsolana/arc-6-catch-up-designing-tokens-that-enforce-rules-3ffk</guid>
      <description>&lt;p&gt;Arc 6 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; was about designing tokens that enforce rules.&lt;/p&gt;

&lt;p&gt;Arc 5 introduced the basics: mints, token accounts, metadata, transfer fees, and non-transferable tokens. Arc 6 pushed that further by asking a more interesting question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if the token could carry more of the product logic itself?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was the real theme of the week.&lt;/p&gt;

&lt;p&gt;Using Token-2022 on devnet, we explored interest-bearing tokens, multi-extension mints, frozen accounts, revocable credentials, and the trade-offs that come with adding more behavior to an asset.&lt;/p&gt;

&lt;p&gt;The whole arc hangs off one idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Token extensions let you compose asset behavior without writing your own on-chain program.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Web2 developers, that is the shift worth noticing.&lt;/p&gt;

&lt;p&gt;In a normal app, rules like interest, fees, access control, and revocation usually live in backend services, database jobs, middleware, or admin tooling.&lt;/p&gt;

&lt;p&gt;With Token-2022, some of those rules can live in the token itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensions are asset features, not app features
&lt;/h2&gt;

&lt;p&gt;Most Web2 products have some kind of internal value system.&lt;/p&gt;

&lt;p&gt;A fintech app might offer yield. A marketplace might charge transaction fees. A learning platform might issue certificates. A SaaS product might gate access based on plan status. A compliance-heavy product might freeze or restrict accounts.&lt;/p&gt;

&lt;p&gt;Normally, those rules live in the application.&lt;/p&gt;

&lt;p&gt;Your backend calculates interest. Your payments layer applies fees. Your database stores membership status. Your admin dashboard revokes credentials. Your API checks whether a user is allowed to transfer, redeem, or access something.&lt;/p&gt;

&lt;p&gt;Token extensions move some of that logic into shared token infrastructure.&lt;/p&gt;

&lt;p&gt;That does not mean every app should become a token app. It means that, when a token is the right primitive, the rules do not have to be reinvented from scratch. You can use audited, reusable building blocks that every client and integration has to respect.&lt;/p&gt;

&lt;p&gt;That is the big idea behind Token-2022.&lt;/p&gt;

&lt;p&gt;The token is no longer just a number in a balance bucket. It can have behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interest-bearing tokens make display different from storage
&lt;/h2&gt;

&lt;p&gt;Arc 6 started with the interest-bearing extension.&lt;/p&gt;

&lt;p&gt;The exercise looked simple: create a token with a 5% interest rate, mint 1,000 tokens, then compare the raw balance with the displayed amount.&lt;/p&gt;

&lt;p&gt;The surprising part is that the ledger balance does not keep changing in the background.&lt;/p&gt;

&lt;p&gt;There is no cron job waking up every minute to update every account. There is no backend service recalculating balances and writing new rows. The token stores the rate and timing information. Readers calculate the interest-adjusted display amount when they need it.&lt;/p&gt;

&lt;p&gt;In Web2 terms, it is like a savings account APY where the displayed balance can grow without the underlying ledger entry being rewritten constantly.&lt;/p&gt;

&lt;p&gt;That is a subtle but important distinction.&lt;/p&gt;

&lt;p&gt;The raw on-chain amount is still the raw amount. The interest-bearing extension changes how that amount is interpreted.&lt;/p&gt;

&lt;p&gt;That is why the arc deliberately made the rate dramatic later. At 5%, the difference is easy to miss. At 150%, you can watch the displayed amount climb much faster and see the model more clearly.&lt;/p&gt;

&lt;p&gt;The lesson was not “make wild APY tokens.” The lesson was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Some token behavior is computed by readers, not stored as repeated balance updates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is a useful mental model to carry into more advanced token work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extensions can be composed
&lt;/h2&gt;

&lt;p&gt;The next step was combining multiple extensions in one mint.&lt;/p&gt;

&lt;p&gt;Arc 6 created a token with three behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;metadata, so the token had a name, symbol, and URI&lt;/li&gt;
&lt;li&gt;transfer fees, so transfers automatically withheld a percentage&lt;/li&gt;
&lt;li&gt;interest, so displayed balances could grow over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where Token-2022 starts to feel different from the original SPL Token Program.&lt;/p&gt;

&lt;p&gt;You are not just creating a token and then building everything else around it. You are deciding which behaviors the token itself should support.&lt;/p&gt;

&lt;p&gt;In Web2 terms, this is closer to designing the schema and rules for a financial product before launch. You decide what the product is allowed to do, what fields it needs, which authorities exist, and which operations should be enforced by the platform.&lt;/p&gt;

&lt;p&gt;The important catch is that extensions are configured up front.&lt;/p&gt;

&lt;p&gt;You cannot casually bolt them on later after the mint already exists. That makes token design feel more like database schema design or API contract design than UI configuration.&lt;/p&gt;

&lt;p&gt;You should think before you mint.&lt;/p&gt;

&lt;p&gt;The other lesson was that extensions operate independently. Transfer fee logic and interest logic can both exist on the same token, but they are not the same feature. One affects what happens during transfer. The other affects how balances are displayed over time.&lt;/p&gt;

&lt;p&gt;Composability is powerful, but it does not remove the need to understand each part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access control can live at the token level
&lt;/h2&gt;

&lt;p&gt;Arc 6 also introduced the default frozen account state.&lt;/p&gt;

&lt;p&gt;That sounds abstract, but the product pattern is familiar.&lt;/p&gt;

&lt;p&gt;Some assets should not be freely usable by default. Stablecoins, security tokens, regulated assets, compliance-gated products, and permissioned marketplaces all need some version of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This account exists, but it is not allowed to move yet.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Web2, that rule might live in your backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if user.kyc_status !== "approved":
    reject transfer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a default-frozen Token-2022 mint, new token accounts start frozen. Transfers and mints fail until the right authority thaws the account.&lt;/p&gt;

&lt;p&gt;That is a very different trust model.&lt;/p&gt;

&lt;p&gt;You are not relying on every frontend, integration, script, or API route to remember the compliance check. The token program enforces the state. If the account is frozen, the transfer does not go through.&lt;/p&gt;

&lt;p&gt;That was the core lesson of the frozen-account exercise: the failure is useful. It proves the rule is enforced below the application layer.&lt;/p&gt;

&lt;p&gt;The pattern is not “add KYC UI.” The pattern is “make unauthorized movement impossible at the token level.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Credentials need different rules from currencies
&lt;/h2&gt;

&lt;p&gt;Arc 6 then moved from financial rules to identity and credentials.&lt;/p&gt;

&lt;p&gt;The revocable credential token combined two extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;non-transferable&lt;/strong&gt;, so the holder could not sell or transfer the credential&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;permanent delegate&lt;/strong&gt;, so the issuer could burn it from the holder’s account if it needed to be revoked&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That combination is a good example of why token design is not one-size-fits-all.&lt;/p&gt;

&lt;p&gt;A currency wants liquidity. A marketplace asset may want fees. A certificate wants the opposite of transferability. Its value depends on being attached to the right holder.&lt;/p&gt;

&lt;p&gt;A bootcamp certificate, professional license, compliance credential, or membership badge is only meaningful if the person who earned it is the person holding it.&lt;/p&gt;

&lt;p&gt;So non-transferability makes sense.&lt;/p&gt;

&lt;p&gt;But credentials also need revocation. A license can expire. A badge can be issued by mistake. A qualification can be withdrawn. A membership can be canceled.&lt;/p&gt;

&lt;p&gt;That is where permanent delegate fits. It gives an authority the ability to burn from a holder’s account without needing that holder to sign the burn transaction.&lt;/p&gt;

&lt;p&gt;That may sound harsh until you map it to Web2.&lt;/p&gt;

&lt;p&gt;If your company issues a certificate, you probably expect the issuer to be able to revoke it. The difference is that, on Solana, that power is expressed through token behavior rather than a private database update.&lt;/p&gt;

&lt;p&gt;This is where Arc 6 became less about “tokens as money” and more about “tokens as stateful credentials.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading configurations matters as much as creating them
&lt;/h2&gt;

&lt;p&gt;One of the most useful parts of the arc was not creating a new token at all.&lt;/p&gt;

&lt;p&gt;It was inspecting the tokens we had already made.&lt;/p&gt;

&lt;p&gt;Arc 6 asked us to compare the mints from the week and read their configurations back out: interest rate authority, transfer fee settings, metadata, freeze authority, default account state, data size, and rent-exempt cost.&lt;/p&gt;

&lt;p&gt;That matters because on-chain configuration is not something you should treat as write-only.&lt;/p&gt;

&lt;p&gt;If you are building seriously, you need to be able to answer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which extensions does this mint use?
Who controls the authorities?
Can those authorities change?
How large is the account?
How much rent-exempt balance does it need?
What rules will every holder be subject to?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the token equivalent of inspecting a production database schema or reviewing cloud infrastructure before launch.&lt;/p&gt;

&lt;p&gt;You do not just create it and hope. You read it back. You compare it. You verify what the chain actually stores.&lt;/p&gt;

&lt;p&gt;Arc 6 also made one trade-off visible: extensions are not free. More extensions mean more account data, and more account data means higher rent-exempt cost.&lt;/p&gt;

&lt;p&gt;That is not necessarily a problem. It is just part of the design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token design is product design
&lt;/h2&gt;

&lt;p&gt;The throughline of Arc 6 was judgment.&lt;/p&gt;

&lt;p&gt;Interest-bearing tokens, transfer fees, frozen accounts, non-transferable credentials, and permanent delegates are all useful, but not universally useful.&lt;/p&gt;

&lt;p&gt;Each one answers a different product question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Should the displayed balance grow over time?
Should each transfer withhold a fee?
Should new accounts start locked?
Should this asset be transferable?
Should the issuer be able to revoke it?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are not just technical choices. They are product choices.&lt;/p&gt;

&lt;p&gt;The mistake is to start with the extension and then hunt for a use case. The better move is to start with the product rule and ask whether a Token-2022 extension already enforces it.&lt;/p&gt;

&lt;p&gt;That is why Arc 6 was a natural follow-up to Arc 5.&lt;/p&gt;

&lt;p&gt;Arc 5 taught the basic token lifecycle. Arc 6 taught that token behavior can be composed.&lt;/p&gt;

&lt;p&gt;Not every app needs custom program code immediately. Sometimes the right answer is to use the token program that already exists, configure it correctly, and understand the trade-offs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing and sharing made the combinations clearer
&lt;/h2&gt;

&lt;p&gt;Arc 6 ended by turning the week into a public explanation.&lt;/p&gt;

&lt;p&gt;That matters because token extensions are easy to list but harder to understand in combination.&lt;/p&gt;

&lt;p&gt;Writing forces the useful question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Which extension combination solves which real product problem?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interest alone is one pattern. Fees plus metadata is another. Default frozen accounts are a compliance pattern. Non-transferable plus permanent delegate is a credential pattern.&lt;/p&gt;

&lt;p&gt;A good write-up does not need to explain every command. It needs to explain the shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here is the Web2 problem.
Here is the token extension combination.
Here is what happened on devnet.
Here is what surprised me.
Here is where this pattern might fit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is how learning becomes useful to someone else.&lt;/p&gt;

&lt;p&gt;The sharing prompt continued that idea: lead with the problem, show concrete takeaways, include the link, and use the community as a conversation rather than a broadcast channel.&lt;/p&gt;

&lt;p&gt;That is especially important in an arc like this. The value is not just “I created another token.” It is “I matched a real-world rule to a token-level feature and proved it works.”&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 6 sets up
&lt;/h2&gt;

&lt;p&gt;Strip Arc 6 back to its core and the main ideas are clear:&lt;/p&gt;

&lt;p&gt;Token-2022 extensions let you add behavior to tokens without writing custom on-chain program code. Interest can affect displayed balances without constantly rewriting ledger balances. Multiple extensions can live on one mint, but they must be planned up front. Frozen accounts support access control. Non-transferable tokens plus permanent delegates support revocable credentials. Reading token configuration back from the chain is part of building responsibly.&lt;/p&gt;

&lt;p&gt;That is the real shift.&lt;/p&gt;

&lt;p&gt;Arc 5 taught us to create and manage tokens.&lt;/p&gt;

&lt;p&gt;Arc 6 taught us to design token behavior.&lt;/p&gt;

&lt;p&gt;From here, the question becomes more practical:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do these tokens interact with real programs on-chain?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is where the next arc goes: taking the token model beyond the CLI and into program-driven interactions.&lt;/p&gt;

&lt;p&gt;Use this post as the map, revisit the Arc 6 challenges when you want the hands-on version, and carry the extension model into what comes next.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>learning</category>
      <category>web3</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>The Rust You Actually Need to Write Your First Anchor Program</title>
      <dc:creator>Vincent Jande</dc:creator>
      <pubDate>Sat, 13 Jun 2026 12:25:48 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/the-rust-you-actually-need-to-write-your-first-anchor-program-4klc</link>
      <guid>https://dev.to/100daysofsolana/the-rust-you-actually-need-to-write-your-first-anchor-program-4klc</guid>
      <description>&lt;p&gt;If you have made it this far in 100 Days of Solana, you have been working in JavaScript and on the command line. You have been calling RPC methods, building instructions, signing transactions, and reading and writing account data in JavaScript, and most recently minting and sending tokens and NFTs from the CLI. Either way, you have been driving Solana with tools that let you assign a value and move on with your life. Soon the ground shifts. You are going to open a file called &lt;code&gt;lib.rs&lt;/code&gt;, and it is going to be Rust, and for a day or two it is going to feel like you forgot how to program.&lt;/p&gt;

&lt;p&gt;That feeling is normal, it is temporary, and it is not a sign you are in the wrong place. Here is the thing nobody says out loud: you do not need to learn all of Rust to write Solana programs. Rust is a big language with a steep reputation, but the slice of it that shows up in an Anchor program is small and repetitive. You will see the same handful of patterns on almost every line. Learn those patterns and the wall turns back into a floor.&lt;/p&gt;

&lt;p&gt;This post is that handful. Not a Rust course, just the parts you need to read your first Anchor program and understand what every line is doing. Next week we start Arc 9, the Anchor introduction, where this all becomes real. This week is about making the language stop being scary before you get there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it feels like a wall
&lt;/h2&gt;

&lt;p&gt;JavaScript is dynamically typed and garbage collected. You write &lt;code&gt;const x = 5&lt;/code&gt;, you never tell anyone it is a number, and when you are done with it the runtime quietly cleans up. The language trusts you and sorts out the consequences at runtime, which is why a typo surfaces as &lt;code&gt;undefined is not a function&lt;/code&gt; three minutes into a demo.&lt;/p&gt;

&lt;p&gt;Rust is the opposite philosophy. It is compiled and statically typed, so every value has a type the compiler knows about before the program ever runs, and it has no garbage collector, so it tracks who is responsible for every piece of memory through a system called ownership. The trade is blunt: Rust makes you front-load the thinking. The compiler refuses to build until the types line up and the ownership rules are satisfied. That is why the first day feels slow. You are paying upfront for something JavaScript billed you for later, usually in production.&lt;/p&gt;

&lt;p&gt;The reassuring part, and this is real, is that the Rust compiler is the most helpful error reporter you have ever worked with. A JavaScript bug explodes at runtime with a vague message and no map. A Rust bug stops at compile time with an error that names the file, the line, the types involved, and very often the exact fix to type. You will spend the Anchor arc reading compiler errors. Get good at reading them slowly instead of panicking at the red text, because nine times out of ten the answer is sitting right there in the message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The anatomy of the file you are about to meet
&lt;/h2&gt;

&lt;p&gt;Before the individual pieces, here is the shape of a minimal Anchor program, taken straight from the &lt;a href="https://www.anchor-lang.com/docs/basics/program-structure" rel="noopener noreferrer"&gt;Anchor program structure docs&lt;/a&gt;:&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;use&lt;/span&gt; &lt;span class="nn"&gt;anchor_lang&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;declare_id!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"11111111111111111111111111111111"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[program]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;hello_anchor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;initialize&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;Initialize&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;data&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="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="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.new_account.data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nd"&gt;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Changed data to: {}!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&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;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Accounts)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Initialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(init,&lt;/span&gt; &lt;span class="nd"&gt;payer&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;signer,&lt;/span&gt; &lt;span class="nd"&gt;space&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;new_account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewAccount&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[account(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Signer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&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;pub&lt;/span&gt; &lt;span class="n"&gt;system_program&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[account]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;NewAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every Anchor program you write next week is a variation on those four blocks. The rest of this post walks through the Rust you need to read them without flinching.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;use&lt;/code&gt; and the import line
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;anchor_lang&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&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 Rust version of an import. &lt;code&gt;use&lt;/code&gt; brings names into scope so you do not have to write the full path every time, and the &lt;code&gt;*&lt;/code&gt; is a glob that pulls in everything from Anchor's &lt;code&gt;prelude&lt;/code&gt;, a curated bundle of the types and macros nearly every program needs. It is the same instinct as importing from a package in JavaScript, just with a &lt;code&gt;::&lt;/code&gt; path separator instead of a dot and slashes. When you later see errors about a type "not found in this scope," a missing &lt;code&gt;use&lt;/code&gt; line is the usual cause. You can read more in the &lt;a href="https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html" rel="noopener noreferrer"&gt;Rust book chapter on packages and modules&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Macros: the lines with &lt;code&gt;!&lt;/code&gt; and &lt;code&gt;#[...]&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the single biggest "what am I looking at" moment for a JavaScript developer, so it gets the most space.&lt;/p&gt;

&lt;p&gt;Rust has macros, which are code that writes code at compile time. They are not functions. You can spot them two ways. A &lt;code&gt;!&lt;/code&gt; after a name, like &lt;code&gt;declare_id!(...)&lt;/code&gt;, &lt;code&gt;msg!(...)&lt;/code&gt;, or &lt;code&gt;require!(...)&lt;/code&gt;, means a macro call. And the bracketed attributes sitting above things, like &lt;code&gt;#[program]&lt;/code&gt;, &lt;code&gt;#[derive(Accounts)]&lt;/code&gt;, and &lt;code&gt;#[account]&lt;/code&gt;, are also macros, the attribute kind, that transform the item directly below them.&lt;/p&gt;

&lt;p&gt;Why this matters: Anchor is mostly macros. The whole point of the framework is that those attributes expand, at compile time, into the hundreds of lines of account-validation and serialization boilerplate that a raw Solana program makes you write by hand. The &lt;a href="https://www.anchor-lang.com/docs/basics/program-structure" rel="noopener noreferrer"&gt;Anchor docs&lt;/a&gt; describe the four you will see constantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;declare_id!&lt;/code&gt; sets your program's on-chain address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[program]&lt;/code&gt; marks the module that holds your instruction handlers. Each public function inside it becomes a callable instruction.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[derive(Accounts)]&lt;/code&gt; goes on a struct to declare the list of accounts an instruction needs, and generates the validation for them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[account]&lt;/code&gt; goes on a struct to mark it as a custom account type your program stores data in. Among other things it stamps an 8-byte discriminator on the account so the program can later prove the account is really one of its own.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need to know how to write a macro. You need to recognize that when you see &lt;code&gt;#[derive(Accounts)]&lt;/code&gt;, an enormous amount of code you did not write is being generated for you, and that this is the framework doing its job. Treat the attributes as configuration you are declaring, not logic you are reading. The &lt;a href="https://doc.rust-lang.org/book/ch20-05-macros.html" rel="noopener noreferrer"&gt;Rust book's macro chapter&lt;/a&gt; is there if you get curious, but it is genuinely optional for the Anchor arc.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Structs: your data shapes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[account]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;NewAccount&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;data&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;struct&lt;/code&gt; is just a named bundle of fields, the same idea as a plain JavaScript object with a fixed shape, except the shape is declared and the compiler enforces it. &lt;code&gt;NewAccount&lt;/code&gt; has one field, &lt;code&gt;data&lt;/code&gt;, of type &lt;code&gt;u64&lt;/code&gt;, an unsigned 64-bit integer. You will define a struct for every kind of account your program stores, and another struct for every instruction's account list (those are the &lt;code&gt;#[derive(Accounts)]&lt;/code&gt; ones). When you read an Anchor program, structs are where you learn what data exists and what each instruction touches.&lt;/p&gt;

&lt;p&gt;A note on number types, because JavaScript hides this from you. JavaScript has one &lt;code&gt;number&lt;/code&gt;. Rust makes you pick: &lt;code&gt;u64&lt;/code&gt; is an unsigned (non-negative) 64-bit integer, &lt;code&gt;i64&lt;/code&gt; is signed, &lt;code&gt;u8&lt;/code&gt; is a byte, and so on. On Solana, &lt;code&gt;u64&lt;/code&gt; is everywhere because token amounts and counts are 64-bit. Picking the type is part of declaring your data, and it is why a counter's field is &lt;code&gt;count: u64&lt;/code&gt; and not just "a number."&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;pub&lt;/code&gt;: who can see this
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pub&lt;/code&gt; means public. By default everything in Rust is private to its module, and &lt;code&gt;pub&lt;/code&gt; opts an item into being visible outside it. In Anchor you will see &lt;code&gt;pub fn&lt;/code&gt; on instruction handlers (the framework needs to call them from outside the module) and &lt;code&gt;pub&lt;/code&gt; on struct fields you want readable. It is access control at the language level, closer to &lt;code&gt;export&lt;/code&gt; in JavaScript modules than to anything in a plain object.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Functions and the return type
&lt;/h2&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;initialize&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;Initialize&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;data&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="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="c1"&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;&lt;code&gt;fn&lt;/code&gt; declares a function. The parameters list their types after a colon, &lt;code&gt;data: u64&lt;/code&gt;, the same "name then type" order you have already seen in the struct. The part that is new is &lt;code&gt;-&amp;gt; Result&amp;lt;()&amp;gt;&lt;/code&gt;, the return type, which leads straight into the most important Rust concept for the week ahead.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;code&gt;Result&lt;/code&gt;, &lt;code&gt;Ok&lt;/code&gt;, and the &lt;code&gt;?&lt;/code&gt; operator: error handling without try/catch
&lt;/h2&gt;

&lt;p&gt;JavaScript handles errors by throwing and catching. Something fails, it throws, and somewhere up the stack a &lt;code&gt;try/catch&lt;/code&gt; maybe deals with it, or maybe it crashes. Nothing in the type system forces you to handle it.&lt;/p&gt;

&lt;p&gt;Rust has no exceptions. Instead, a function that can fail returns a &lt;code&gt;Result&lt;/code&gt;, a type that is either &lt;code&gt;Ok(value)&lt;/code&gt; for success or &lt;code&gt;Err(error)&lt;/code&gt; for failure. Every Anchor instruction handler returns &lt;code&gt;Result&amp;lt;()&amp;gt;&lt;/code&gt;, which reads as "this either succeeds with nothing meaningful to return, the empty &lt;code&gt;()&lt;/code&gt;, or it fails with an error." That is why every successful handler ends with &lt;code&gt;Ok(())&lt;/code&gt;: you are explicitly returning the success case. The &lt;a href="https://www.anchor-lang.com/docs/basics/program-structure" rel="noopener noreferrer"&gt;Anchor docs&lt;/a&gt; note that all Anchor instruction handlers return this &lt;code&gt;Result&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;The companion to &lt;code&gt;Result&lt;/code&gt; is the &lt;code&gt;?&lt;/code&gt; operator, and it is worth learning early because it is everywhere. When you call something that returns a &lt;code&gt;Result&lt;/code&gt; and write a &lt;code&gt;?&lt;/code&gt; after 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;let&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;something_that_can_fail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it means "if this is &lt;code&gt;Ok&lt;/code&gt;, unwrap the value and keep going; if it is &lt;code&gt;Err&lt;/code&gt;, stop this function right now and return that error to the caller." It is the same job a &lt;code&gt;try/catch&lt;/code&gt; does, compressed into one character, and it is how Anchor programs bubble failures up to the runtime cleanly. When you see &lt;code&gt;?&lt;/code&gt;, read it as "this line might bail out early, and that is intentional."&lt;/p&gt;

&lt;p&gt;You will also meet &lt;code&gt;Option&lt;/code&gt;, the cousin of &lt;code&gt;Result&lt;/code&gt;: it is &lt;code&gt;Some(value)&lt;/code&gt; or &lt;code&gt;None&lt;/code&gt;, and it is how Rust represents "this might be absent" instead of JavaScript's &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;. The reason Rust almost never has null-pointer surprises is that absence is a type you are forced to handle, not a landmine.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. References and &lt;code&gt;&amp;amp;mut&lt;/code&gt;: the ownership stuff
&lt;/h2&gt;

&lt;p&gt;Here is the concept with no JavaScript equivalent, and the one that produces the most head-scratching. Rust tracks ownership of data. Every value has one owner, and when you want to let other code use a value without handing over ownership, you lend it out with a reference, written &lt;code&gt;&amp;amp;&lt;/code&gt;. A plain &lt;code&gt;&amp;amp;&lt;/code&gt; is a read-only borrow. &lt;code&gt;&amp;amp;mut&lt;/code&gt; is a mutable borrow, meaning "I am lending you this and you are allowed to change it."&lt;/p&gt;

&lt;p&gt;You will see this constantly in handlers:&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;let&lt;/span&gt; &lt;span class="n"&gt;counter&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.counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="py"&gt;.count&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;code&gt;&amp;amp;mut ctx.accounts.counter&lt;/code&gt; as "get a mutable borrow of the counter account so I can write to it." Without &lt;code&gt;&amp;amp;mut&lt;/code&gt;, you would have a read-only view and the compiler would reject the assignment. The rule the compiler enforces, and the source of most early borrow-checker errors, is that you can have many readers or one writer, but not both at once. That sounds restrictive until you realize it is the rule that makes whole categories of bugs impossible.&lt;/p&gt;

&lt;p&gt;For the Anchor arc you do not need the deep theory. You need to recognize that &lt;code&gt;&amp;amp;&lt;/code&gt; means "borrowed, look but do not take," &lt;code&gt;&amp;amp;mut&lt;/code&gt; means "borrowed, allowed to change," and that when the compiler complains about borrowing it is protecting you from two pieces of code stepping on the same data. When you genuinely want to understand it, the &lt;a href="https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html" rel="noopener noreferrer"&gt;ownership chapter of the Rust book&lt;/a&gt; is the clearest explanation written, and the &lt;code&gt;Pubkey&lt;/code&gt; types being small and &lt;code&gt;Copy&lt;/code&gt; (cheap to duplicate, so you often pass them by value) is a footnote you will appreciate later.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Custom errors with enums
&lt;/h2&gt;

&lt;p&gt;When your program needs to reject something with a meaningful reason, you define an error type as an &lt;code&gt;enum&lt;/code&gt;, a type that is one of a fixed set of named variants:&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="nd"&gt;#[error_code]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;CounterError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[msg(&lt;/span&gt;&lt;span class="s"&gt;"The counter has reached its maximum value."&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Overflow&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;An &lt;code&gt;enum&lt;/code&gt; in Rust is more powerful than a JavaScript constant list, but for now read it as "a closed set of named options." The &lt;code&gt;#[error_code]&lt;/code&gt; attribute is Anchor wiring it into the program's error system, and the &lt;code&gt;#[msg(...)]&lt;/code&gt; lines are the human-readable strings that show up in logs when that error fires. You then raise one with the &lt;code&gt;require!&lt;/code&gt; macro or by returning &lt;code&gt;Err(...)&lt;/code&gt;. The &lt;a href="https://www.anchor-lang.com/docs/features/errors" rel="noopener noreferrer"&gt;Anchor errors guide&lt;/a&gt; covers the full pattern, and you will use it the moment your program needs to say no to bad input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;Read that minimal program one more time and notice you can now name every piece. &lt;code&gt;use&lt;/code&gt; pulls in Anchor. &lt;code&gt;declare_id!&lt;/code&gt; is a macro setting the address. &lt;code&gt;#[program]&lt;/code&gt; is an attribute macro marking the handler module. &lt;code&gt;initialize&lt;/code&gt; is a public function taking a &lt;code&gt;Context&lt;/code&gt; and some data, returning a &lt;code&gt;Result&lt;/code&gt;, ending in &lt;code&gt;Ok(())&lt;/code&gt;. The &lt;code&gt;#[derive(Accounts)]&lt;/code&gt; struct declares the accounts. The &lt;code&gt;#[account]&lt;/code&gt; struct declares the stored data shape, with a &lt;code&gt;u64&lt;/code&gt; field. None of it is mysterious anymore. It is the same eight ideas, arranged.&lt;/p&gt;

&lt;p&gt;That is the whole trick this week. You are not learning Rust the way a systems programmer learns Rust. You are learning to read a specific, repetitive dialect of it, and the vocabulary is small: imports, macros, structs, &lt;code&gt;pub&lt;/code&gt;, functions, &lt;code&gt;Result&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt;, references, and enums. Every program in the arc ahead is built from those.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually get through the week
&lt;/h2&gt;

&lt;p&gt;A few habits that will save you real time.&lt;/p&gt;

&lt;p&gt;Read the compiler errors top to bottom, slowly. Rust errors are long because they are thorough, not because they are angry. The first error is usually the real one; later errors are often just fallout. Fix the top one, recompile, repeat.&lt;/p&gt;

&lt;p&gt;Compile early and often. Do not write thirty lines and then build. Write a few, run &lt;code&gt;anchor build&lt;/code&gt;, see if it is happy, continue. Short feedback loops turn a scary language into a conversation.&lt;/p&gt;

&lt;p&gt;When the borrow checker fights you, stop and ask who owns this and who is borrowing it, rather than randomly adding and removing &lt;code&gt;&amp;amp;&lt;/code&gt; until it builds. The error message almost always tells you which rule you broke.&lt;/p&gt;

&lt;p&gt;And keep one tab open on the &lt;a href="https://www.anchor-lang.com/docs/basics/program-structure" rel="noopener noreferrer"&gt;Anchor program structure page&lt;/a&gt; and one on the &lt;a href="https://doc.rust-lang.org/book/" rel="noopener noreferrer"&gt;Rust book&lt;/a&gt;. You will not read either cover to cover. You will look up exactly the thing in front of you, which is the right way to learn a language you are using rather than studying.&lt;/p&gt;

&lt;p&gt;Next week the challenges move into Arc 9, the Anchor introduction, and these blog posts move with them. We leave the language topic behind here, not the program itself, and dig into Anchor properly: how accounts really work, what a Program Derived Address is and why it changes everything, and how the constraint system turns a struct into a security model. The daily challenges keep going as always; it is just this post's focus on the Rust language that wraps up today. For now, the goal is smaller and more important. Open &lt;code&gt;lib.rs&lt;/code&gt;, recognize the pieces, and let the compiler teach you. You already did the hard part by getting here.&lt;/p&gt;

&lt;p&gt;And if you have not joined the challenge yet, this is the moment. We are about to start writing real Solana programs, which is the part everything so far has been building toward. Jump in at &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;mlh.link/solana-100&lt;/a&gt; and build it with us.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Arc 5 Catch-Up: Solana Token Fundamentals Explained for Web2 Developers</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Mon, 08 Jun 2026 12:21:27 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-5-catch-up-solana-token-fundamentals-explained-for-web2-developers-3hfn</link>
      <guid>https://dev.to/100daysofsolana/arc-5-catch-up-solana-token-fundamentals-explained-for-web2-developers-3hfn</guid>
      <description>&lt;p&gt;Arc 5 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; opened Epoch 2 by moving from foundation work into programmable assets.&lt;/p&gt;

&lt;p&gt;Epoch 1 gave us the basics: wallets, reads, transactions, accounts, and raw on-chain data. Arc 5 put those ideas to work on one of Solana’s most important primitives: tokens.&lt;/p&gt;

&lt;p&gt;The arc theme was &lt;strong&gt;Token Fundamentals&lt;/strong&gt;. The Web2 bridge was incentive systems: reward points, marketplace credits, loyalty balances, badges, fees, and transfer rules.&lt;/p&gt;

&lt;p&gt;The whole arc hangs off one idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On Solana, a token is not just a balance. It is a set of rules enforced by a shared on-chain program.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the big shift for Web2 developers.&lt;/p&gt;

&lt;p&gt;In a normal app, token-like systems often live in your database and your backend logic. You design tables, write endpoints, check permissions, prevent double-spends, calculate fees, and decide which transfers are allowed.&lt;/p&gt;

&lt;p&gt;On Solana, much of that machinery already exists in audited token programs. Arc 5 was about learning to use those programs before trying to invent your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tokens are incentive systems with stronger guarantees
&lt;/h2&gt;

&lt;p&gt;If you have built Web2 products, you have probably seen token-like systems already.&lt;/p&gt;

&lt;p&gt;A marketplace might have seller credits. A learning platform might have completion badges. A game might have in-app currency. A SaaS product might track usage credits. A community might issue reputation points or membership status.&lt;/p&gt;

&lt;p&gt;Those systems usually depend on application code.&lt;/p&gt;

&lt;p&gt;Your backend decides who has what. Your database stores the balances. Your API enforces the rules. If you want transfer fees, you write the fee logic. If you want a badge that cannot be transferred, you add checks in your application.&lt;/p&gt;

&lt;p&gt;That works, but the rules live in your system.&lt;/p&gt;

&lt;p&gt;Solana changes the shape of that design. With the SPL Token Program and Token-2022, many token rules can be enforced at the program level. That means clients, scripts, and other programs have to follow the same rules.&lt;/p&gt;

&lt;p&gt;That is what made Arc 5 interesting. We were not just creating balances. We were exploring what happens when economic rules become part of the asset itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  A mint defines the token
&lt;/h2&gt;

&lt;p&gt;The first token in Arc 5 was deliberately simple.&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;spl-token&lt;/code&gt; CLI on devnet, we created a token, created an account to hold it, minted 100 units, and inspected the result.&lt;/p&gt;

&lt;p&gt;That sounds straightforward, but it introduces the most important token distinction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A mint defines the token. A token account holds someone’s balance of that token.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are coming from Web2, the mint is a little like the currency definition or product catalog entry. It says what the token is: its supply, decimals, and mint authority.&lt;/p&gt;

&lt;p&gt;A token account is more like an individual balance bucket. It says how many of that token a particular owner holds.&lt;/p&gt;

&lt;p&gt;That distinction matters because you do not receive tokens directly “into a wallet” in the way newcomers often imagine. For each token type, a wallet needs a token account to hold that specific token.&lt;/p&gt;

&lt;p&gt;A wallet is more like a filing cabinet. Each token account is a folder inside it.&lt;/p&gt;

&lt;p&gt;That is the first Solana token model to carry forward:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mint account: what the token is.&lt;br&gt;
Token account: who holds how much of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once that clicks, token balances stop feeling like magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Token-2022 makes the token itself more expressive
&lt;/h2&gt;

&lt;p&gt;Arc 5 then moved from the original SPL Token Program to Token-2022, also known as the Token Extensions Program.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;The original SPL Token Program gives you the classic token basics: create a mint, create token accounts, mint supply, transfer, burn, inspect balances.&lt;/p&gt;

&lt;p&gt;Token-2022 keeps that familiar foundation but adds extensions: metadata, transfer fees, non-transferable tokens, and other features that would otherwise require more custom infrastructure.&lt;/p&gt;

&lt;p&gt;In Arc 5, the first Token-2022 step was a branded token.&lt;/p&gt;

&lt;p&gt;We created a mint with metadata enabled, set decimals, and wrote a name, symbol, and URI directly onto the mint.&lt;/p&gt;

&lt;p&gt;That is important because metadata gives a token identity. Without it, a token is mostly an address and some numbers. With it, the token becomes recognizable: name, symbol, and a pointer to more information.&lt;/p&gt;

&lt;p&gt;For Web2 developers, this is like moving from “database row with an ID” to “product with a name, SKU, description, and image.”&lt;/p&gt;

&lt;p&gt;The technical difference is that the identity lives with the token infrastructure, not just in an off-chain app database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Associated Token Accounts are the default balance bucket
&lt;/h2&gt;

&lt;p&gt;Arc 5 also introduced Associated Token Accounts, or ATAs.&lt;/p&gt;

&lt;p&gt;An ATA is a deterministic token account derived from a wallet address and a mint address. In plain English:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For this wallet and this token, there is a predictable default account that holds the balance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is useful because it avoids the chaos of every wallet having arbitrary token accounts everywhere.&lt;/p&gt;

&lt;p&gt;In Web2 terms, imagine if every user/product pair had a predictable balance record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;balance = getBalance(userId, tokenId)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You do not have to guess where the balance should live. You can derive it.&lt;/p&gt;

&lt;p&gt;That is why ATAs show up constantly in Solana token work. They are the standard place to look when asking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How much of this token does this wallet hold?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arc 5 used ATAs early because they are not an advanced detail. They are part of the everyday token model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transfer fees move marketplace logic into the token program
&lt;/h2&gt;

&lt;p&gt;The most commercially familiar extension in Arc 5 was transfer fees.&lt;/p&gt;

&lt;p&gt;In a Web2 marketplace, a payment or transaction fee usually lives in backend code. Someone transfers value, your system calculates a percentage, applies a cap, updates balances, and records the fee.&lt;/p&gt;

&lt;p&gt;With Token-2022 transfer fees, that rule can live in the token itself.&lt;/p&gt;

&lt;p&gt;In Arc 5, we created a token with a transfer fee. When tokens moved, a percentage was withheld automatically in the recipient’s token account. The recipient could not spend those withheld tokens. The fee authority could later collect them.&lt;/p&gt;

&lt;p&gt;That is the important part:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The fee is enforced by the token program, not by a UI convention or backend middleware.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That changes the trust model. A different client, script, or integration cannot simply “forget” to charge the fee. The rule is part of the token’s transfer behavior.&lt;/p&gt;

&lt;p&gt;There was also a very real developer gotcha: decimals.&lt;/p&gt;

&lt;p&gt;Transfer fee maximums are specified in base units, not display units. If your token has decimals, the number you type may not mean what you think it means. That kind of detail bites everyone eventually, and Arc 5 surfaced it early.&lt;/p&gt;

&lt;p&gt;The broader lesson is simple: token economics are product design, but the implementation is exact. Percentages, caps, decimals, authorities, and units all matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Non-transferable tokens are useful because they fail
&lt;/h2&gt;

&lt;p&gt;Arc 5 also explored non-transferable tokens, sometimes described as soulbound tokens.&lt;/p&gt;

&lt;p&gt;The exercise was intentionally odd: create a non-transferable token, mint it, then try to transfer it and watch the transfer fail.&lt;/p&gt;

&lt;p&gt;That failure was the point.&lt;/p&gt;

&lt;p&gt;A normal token is valuable partly because it can move. A non-transferable token is valuable because it cannot.&lt;/p&gt;

&lt;p&gt;That makes sense for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verified badges&lt;/li&gt;
&lt;li&gt;course-completion certificates&lt;/li&gt;
&lt;li&gt;membership credentials&lt;/li&gt;
&lt;li&gt;KYC or compliance status&lt;/li&gt;
&lt;li&gt;reputation markers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In those cases, transferability would undermine the meaning of the token. A certificate is not useful if someone else can buy it from you. A verified badge loses value if it can be traded.&lt;/p&gt;

&lt;p&gt;The key lesson was that the restriction is enforced by the token program.&lt;/p&gt;

&lt;p&gt;You are not relying on your frontend to hide a transfer button. You are not relying on an API route to reject the request. The token itself cannot be transferred under the rules of the program.&lt;/p&gt;

&lt;p&gt;But it can still be burned.&lt;/p&gt;

&lt;p&gt;That distinction matters. Non-transferable does not mean indestructible. It means the holder cannot transfer it to someone else. Burning still lets supply be reduced or credentials be revoked, depending on how the system is designed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lifecycle is the skill
&lt;/h2&gt;

&lt;p&gt;Arc 5 was not only about individual token features. It also reinforced a repeatable lifecycle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create → configure → mint → transfer → collect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pattern showed up again and again.&lt;/p&gt;

&lt;p&gt;Create the mint. Configure its rules. Create the account that will hold the token. Mint supply. Transfer some tokens. Inspect what happened. Collect withheld fees if the extension uses them.&lt;/p&gt;

&lt;p&gt;That repetition matters because token work can feel fiddly at first.&lt;/p&gt;

&lt;p&gt;There are mints and token accounts. Original SPL Token and Token-2022. Display units and base units. Metadata and authorities. Transfer fees and withheld balances. Non-transferable rules and burn behavior.&lt;/p&gt;

&lt;p&gt;The way through that complexity is not to memorize every flag. It is to recognize the lifecycle.&lt;/p&gt;

&lt;p&gt;Once you can see where you are in the lifecycle, the commands start to make more sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building in public is part of the work
&lt;/h2&gt;

&lt;p&gt;Arc 5 ended, like the earlier arcs, by writing and sharing.&lt;/p&gt;

&lt;p&gt;But this week had more to show.&lt;/p&gt;

&lt;p&gt;By the end of the arc, we had created tokens, branded them, minted supply, applied fees, collected withheld tokens, and tested transfer restrictions. That is a proper artifact, not just a learning note.&lt;/p&gt;

&lt;p&gt;The writing prompt asked us to explain what clicked: why Token-2022 matters, why protocol-enforced rules are different from app-level checks, and what surprised us about token design.&lt;/p&gt;

&lt;p&gt;The sharing prompt pushed us to show one concrete moment: the first mint, metadata on Explorer, fees collected by the program, or a rejected non-transferable transfer.&lt;/p&gt;

&lt;p&gt;That is good developer storytelling because it gives people proof.&lt;/p&gt;

&lt;p&gt;Not “I learned tokens.”&lt;/p&gt;

&lt;p&gt;More like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I created a Token-2022 mint with metadata.
I transferred it.
The program withheld the fee.
Here is the Explorer screenshot.
Here is what surprised me.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is much more useful to the next person trying to understand the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 5 sets up
&lt;/h2&gt;

&lt;p&gt;Strip Arc 5 back to its core and the main ideas are clear:&lt;/p&gt;

&lt;p&gt;A mint defines a token. Token accounts hold balances. Associated Token Accounts give wallets a predictable balance account for each token. Token-2022 adds extensions such as metadata, transfer fees, and non-transferable behavior. Those extensions move rules that often live in Web2 backend code into shared on-chain programs.&lt;/p&gt;

&lt;p&gt;That is the real shift.&lt;/p&gt;

&lt;p&gt;In Web2, token-like systems often depend on your app enforcing the rules.&lt;/p&gt;

&lt;p&gt;On Solana, token programs can enforce many of those rules directly.&lt;/p&gt;

&lt;p&gt;That is what makes tokens such a useful first step after the foundation epoch. They take identity, accounts, transactions, and state, then combine them into something you can actually design with: incentives, fees, credentials, rewards, and asset behavior.&lt;/p&gt;

&lt;p&gt;Arc 5 opened Epoch 2 by showing that tokens are not just things you hold. They are programmable assets with rules.&lt;/p&gt;

&lt;p&gt;Use this post as the map, revisit the Arc 5 challenges when you want the hands-on version, and carry the token model into the next arc.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>learning</category>
    </item>
    <item>
      <title>Arc 4 Catch-Up: Solana’s Account Model Explained for Web2 Developer</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Mon, 08 Jun 2026 12:06:13 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-4-catch-up-solanas-account-model-explained-for-web2-developer-1hh4</link>
      <guid>https://dev.to/100daysofsolana/arc-4-catch-up-solanas-account-model-explained-for-web2-developer-1hh4</guid>
      <description>&lt;p&gt;Arc 4 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; was about the Solana account model: what state actually looks like on-chain, who can change it, and how developers read and decode it.&lt;/p&gt;

&lt;p&gt;Arc 3 gave us the transaction model. We learned that transactions are how Solana changes state.&lt;/p&gt;

&lt;p&gt;Arc 4 asked the next question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where does that state live?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On Solana, the answer is accounts.&lt;/p&gt;

&lt;p&gt;That sounds simple, but it is one of the biggest mental model shifts in the whole foundation epoch. A Solana account is not just a wallet. It is not just a user profile. It is not just a row in a database.&lt;/p&gt;

&lt;p&gt;On Solana, almost everything is an account: wallets, programs, token mints, token accounts, sysvars, and application state.&lt;/p&gt;

&lt;p&gt;The whole arc hangs off one idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On Solana, accounts are the containers that hold state, code, ownership, and balance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once that clicks, the rest of Solana starts to fit together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accounts are where state lives
&lt;/h2&gt;

&lt;p&gt;In a Web2 app, state usually lives in places you control.&lt;/p&gt;

&lt;p&gt;User data might be in Postgres. Files might be in object storage. sessions might be in Redis. App code runs on your servers or serverless functions. Access control often lives in middleware, business logic, row-level security, or API permissions.&lt;/p&gt;

&lt;p&gt;Solana rearranges that picture.&lt;/p&gt;

&lt;p&gt;State lives in accounts. Programs are accounts too. Wallets are accounts. Token data is stored in accounts. Even bits of cluster state are exposed through special accounts.&lt;/p&gt;

&lt;p&gt;That is what people mean when they say “everything is an account.”&lt;/p&gt;

&lt;p&gt;It does not mean every account behaves the same way. A wallet account, a token mint, and an executable program account do different jobs. But they share the same basic structure, and that shared structure is what Arc 4 unpacked.&lt;/p&gt;

&lt;p&gt;A Solana account has five core fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;lamports&lt;/strong&gt;: how much SOL the account holds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;data&lt;/strong&gt;: the bytes stored in the account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;owner&lt;/strong&gt;: the program allowed to modify the account’s data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;executable&lt;/strong&gt;: whether the account contains runnable program code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rentEpoch&lt;/strong&gt;: a legacy rent-related field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the foundation. Different accounts become meaningful because those fields are set differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The owner field is the security model
&lt;/h2&gt;

&lt;p&gt;For Web2 developers, the &lt;code&gt;owner&lt;/code&gt; field is one of the most important ideas to get right.&lt;/p&gt;

&lt;p&gt;It does not mean “the human who owns this account.”&lt;/p&gt;

&lt;p&gt;It means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which program is allowed to change this account’s data?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes &lt;code&gt;owner&lt;/code&gt; much closer to a low-level access-control rule than a user-facing ownership label.&lt;/p&gt;

&lt;p&gt;In a Web2 app, you might enforce permissions in your API layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can this user update this row?
Can this service write to this bucket?
Can this job mutate this record?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Solana, the runtime enforces a stricter version of that idea. Only the owning program can modify an account’s data. If a token account is owned by the Token Program, your random application code cannot just rewrite it. If your program owns a data account, then your program defines how that data can change.&lt;/p&gt;

&lt;p&gt;That is why the account model matters so much.&lt;/p&gt;

&lt;p&gt;Transactions may carry the instructions, but accounts define the state those instructions act on. The &lt;code&gt;owner&lt;/code&gt; field helps decide which program has authority over that state.&lt;/p&gt;

&lt;p&gt;That is not middleware. It is part of the runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programs are accounts too
&lt;/h2&gt;

&lt;p&gt;One of the more useful realizations in Arc 4 was that programs are not magical things floating somewhere outside the account model.&lt;/p&gt;

&lt;p&gt;Programs are accounts.&lt;/p&gt;

&lt;p&gt;The difference is that program accounts have &lt;code&gt;executable: true&lt;/code&gt;. Their data contains compiled program code, and their owner is a loader program rather than the System Program.&lt;/p&gt;

&lt;p&gt;That is a strange idea at first, but there is a useful Web2 analogy:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code and the data are separate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A web server might run application code, then read and write rows in a database. On Solana, a program account contains executable code, while separate data accounts hold the state that program works with.&lt;/p&gt;

&lt;p&gt;That separation becomes crucial later.&lt;/p&gt;

&lt;p&gt;When you write Solana programs, you are not usually storing mutable state inside the program itself. You are writing code that receives accounts, checks them, reads their data, and changes the accounts it is allowed to change.&lt;/p&gt;

&lt;p&gt;So Arc 4 quietly set up one of the most important Solana development habits:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Think in accounts, not objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your program logic is only half the story. The accounts passed into that program are the other half.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading an account is like opening a file
&lt;/h2&gt;

&lt;p&gt;Arc 4 started with inspection.&lt;/p&gt;

&lt;p&gt;Using the Solana CLI, we looked at a normal wallet account, the SPL Token Program, and the System Program side by side. That made the shared structure visible.&lt;/p&gt;

&lt;p&gt;The Web2 analogy here is not perfect, but it helps:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reading a Solana account is a bit like opening a file or inspecting a database row.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can see its balance. You can see whether it has data. You can see who owns it. You can see whether it is executable.&lt;/p&gt;

&lt;p&gt;But unlike a normal database row, account data often starts as raw bytes. The network can give you the bytes, but it does not automatically know how you want to interpret them.&lt;/p&gt;

&lt;p&gt;That is where account decoding comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raw bytes are where this gets real
&lt;/h2&gt;

&lt;p&gt;Arc 4 was the most “under the hood” week of Epoch 1, and the account decoding work was the reason.&lt;/p&gt;

&lt;p&gt;At first, an account’s &lt;code&gt;data&lt;/code&gt; field can look like base64 gibberish. That is not because it is meaningless. It is because you are looking at serialized bytes.&lt;/p&gt;

&lt;p&gt;The challenge was to decode those bytes and prove that the same account data could be understood in three ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;with a typed decoder from &lt;code&gt;@solana-program/token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;manually, byte by byte, using &lt;code&gt;DataView&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;through the RPC’s &lt;code&gt;jsonParsed&lt;/code&gt; encoding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is not just busywork. It is the moment where “on-chain data” stops being abstract.&lt;/p&gt;

&lt;p&gt;If you have ever parsed a binary file format, decoded a network packet, or mapped bytes into a struct, this is the same kind of work. The data is there. Your job is to know its layout.&lt;/p&gt;

&lt;p&gt;Arc 4 introduced several ideas that will keep coming back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Borsh serialization&lt;/strong&gt;: how structured data becomes bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;little-endian numbers&lt;/strong&gt;: a common source of decoding bugs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;base64&lt;/strong&gt;: how RPC often returns account data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hex&lt;/strong&gt;: useful for debugging raw bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;base58&lt;/strong&gt;: how Solana addresses are displayed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was the hardest part of the arc, but it was also the most transferable.&lt;/p&gt;

&lt;p&gt;If you can decode account data, you are no longer limited to what a block explorer chooses to show you. You can inspect state yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The System Program is the account clerk
&lt;/h2&gt;

&lt;p&gt;Arc 4 also took us one layer deeper into the system itself.&lt;/p&gt;

&lt;p&gt;The System Program is one of the native programs that makes Solana work. A useful way to think about it is as the clerk that creates and labels accounts.&lt;/p&gt;

&lt;p&gt;It can create accounts. It can assign ownership. It can allocate space. It handles basic SOL transfers.&lt;/p&gt;

&lt;p&gt;That makes it feel a little like part of an operating system. Not your application logic, but the lower-level machinery that makes files, processes, permissions, and memory possible.&lt;/p&gt;

&lt;p&gt;Sysvar accounts are another useful piece of that system layer. They expose read-only cluster state, such as clock or rent information, through accounts.&lt;/p&gt;

&lt;p&gt;A rough Web2 analogy would be &lt;code&gt;/proc&lt;/code&gt; on Linux: system information exposed as readable data.&lt;/p&gt;

&lt;p&gt;Again, the analogy is not exact. But it helps place the idea. Solana’s account model is not only for application state. It is also how the system exposes information about itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Block explorers are blockchain DevTools
&lt;/h2&gt;

&lt;p&gt;After decoding account data by hand, Arc 4 stepped back and looked at Explorer again.&lt;/p&gt;

&lt;p&gt;That was useful because block explorers are not just places to paste transaction signatures. They are the debugging surface for a public blockchain.&lt;/p&gt;

&lt;p&gt;Solana Explorer, Solscan, and similar tools are basically UI wrappers around public chain data. They show accounts, transactions, owners, program logs, balances, token data, and instruction details.&lt;/p&gt;

&lt;p&gt;If you are used to browser DevTools, database consoles, or log dashboards, this is the role explorers play.&lt;/p&gt;

&lt;p&gt;They help you answer questions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What account am I looking at?
Who owns it?
Is it executable?
What data does it hold?
Which transaction changed it?
What logs were emitted?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading explorers fluently is a real Solana development skill. It lets you move between your code, RPC calls, transaction signatures, and on-chain state without guessing.&lt;/p&gt;

&lt;p&gt;Arc 4 made that explicit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing helps the model settle
&lt;/h2&gt;

&lt;p&gt;Arc 4 ended the same way the earlier arcs did: by writing and sharing.&lt;/p&gt;

&lt;p&gt;That matters especially for this arc because the account model is not something you fully absorb by reading one definition. It takes a few passes.&lt;/p&gt;

&lt;p&gt;You inspect an account. You compare it with another one. You decode bytes. You look at a program. You notice that the same five fields keep showing up. Then you try to explain it to someone else.&lt;/p&gt;

&lt;p&gt;That is when the model starts to stick.&lt;/p&gt;

&lt;p&gt;Writing about the account model forces useful questions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What does owner really mean?
How is a program also an account?
Where does state live?
Why is account data raw bytes?
What does rent-exempt storage mean?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those questions are exactly the point.&lt;/p&gt;

&lt;p&gt;The goal is not polished certainty. The goal is a clearer map.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 4 sets up
&lt;/h2&gt;

&lt;p&gt;Arc 4 closed Epoch 1 by giving us the data model underneath everything else.&lt;/p&gt;

&lt;p&gt;Strip it back to the core ideas:&lt;/p&gt;

&lt;p&gt;Accounts are where Solana state lives. Every account has the same basic fields. The &lt;code&gt;owner&lt;/code&gt; field controls who can change account data. Programs are executable accounts. Application state lives in separate data accounts. Raw account data is bytes, and decoding those bytes is a core developer skill.&lt;/p&gt;

&lt;p&gt;That brings the foundation epoch together.&lt;/p&gt;

&lt;p&gt;Arc 1 gave us identity: keys, wallets, addresses, and devnet SOL.&lt;/p&gt;

&lt;p&gt;Arc 2 gave us reads: RPC calls, balances, transaction history, and public blockchain data.&lt;/p&gt;

&lt;p&gt;Arc 3 gave us writes: signed transactions, confirmation, and failure modes.&lt;/p&gt;

&lt;p&gt;Arc 4 gave us state: accounts, ownership, executable programs, raw data, and decoding.&lt;/p&gt;

&lt;p&gt;From here, the next step is natural.&lt;/p&gt;

&lt;p&gt;Once you understand identity, reads, writes, and state, you are ready to build with the programs that own and modify those accounts.&lt;/p&gt;

&lt;p&gt;Use this post as the map, revisit the Arc 4 challenges when you want the hands-on version, and carry the account model into what comes next.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>learning</category>
    </item>
    <item>
      <title>Arc 2 Catch-Up: Reading Solana Like a Public Database</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Mon, 08 Jun 2026 11:59:40 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-2-catch-up-reading-solana-like-a-public-database-cn2</link>
      <guid>https://dev.to/100daysofsolana/arc-2-catch-up-reading-solana-like-a-public-database-cn2</guid>
      <description>&lt;p&gt;Arc 2 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; was about learning to read from the blockchain.&lt;/p&gt;

&lt;p&gt;In Arc 1, we created wallets, worked with addresses, got devnet SOL, and started building the basic identity layer we need to interact with Solana. Arc 2 shifted the focus from identity to information.&lt;/p&gt;

&lt;p&gt;What can you read from Solana? How do you ask for it? What comes back? And how is that different from fetching data from a normal Web2 API or querying a database?&lt;/p&gt;

&lt;p&gt;The whole arc hangs off one useful mental model:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Solana is a massive public database where every table is readable and every query is free.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is not a perfect technical description, but it is a useful starting point. If you are coming from Web2, Arc 2 is where Solana starts to feel less like an abstract blockchain and more like a data source you can inspect, query, and build interfaces around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading from Solana feels familiar
&lt;/h2&gt;

&lt;p&gt;Reading from Solana is the easy part to map onto Web2.&lt;/p&gt;

&lt;p&gt;You ask for a wallet balance. You fetch recent transactions for an address. You inspect account data. The network answers.&lt;/p&gt;

&lt;p&gt;That shape feels familiar if you have ever called a REST API, queried a database, or built a dashboard around backend data.&lt;/p&gt;

&lt;p&gt;In Arc 2, the first read was deliberately small: use &lt;code&gt;@solana/kit&lt;/code&gt;, connect to devnet, and call &lt;code&gt;getBalance&lt;/code&gt; for a wallet address.&lt;/p&gt;

&lt;p&gt;That one call teaches more than it first appears to.&lt;/p&gt;

&lt;p&gt;There is no user session. No API key. No private dashboard. No auth header. The address is public, the balance is public, and the network can return it directly.&lt;/p&gt;

&lt;p&gt;That is the first shift:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On Solana, public data is public by default.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For Web2 developers, that can feel backwards. In most application databases, data starts private. You expose it through APIs, auth rules, dashboards, and permissions. On Solana, a lot of the base data is already out in the open. The job of your application is often not to unlock access, but to make public data understandable and useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  A wallet balance is just the first query
&lt;/h2&gt;

&lt;p&gt;Once you can fetch a balance, the next step is obvious: fetch activity.&lt;/p&gt;

&lt;p&gt;Arc 2 moved from &lt;code&gt;getBalance&lt;/code&gt; to recent transaction history. Using &lt;code&gt;getSignaturesForAddress&lt;/code&gt;, we queried the most recent transactions for an address and displayed the signature, slot, timestamp, and status.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;getBalance&lt;/code&gt; is like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /users/:id/balance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then recent transactions feel more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /users/:id/transactions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern is familiar. The data is not.&lt;/p&gt;

&lt;p&gt;A transaction signature is not just a random ID. It is the identifier you can use to look up what happened. A slot is not quite a timestamp; it is Solana’s way of ordering batches of work over time. A block time gives you a familiar Unix timestamp when one is available.&lt;/p&gt;

&lt;p&gt;This is where Solana starts to feel like a database with its own vocabulary.&lt;/p&gt;

&lt;p&gt;You can query it. You can display the result. You can build UI around it. But the records you get back are blockchain records, not rows from your own application database.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;You are not asking your backend, “What did this user do in my app?” You are asking the network, “What activity is visible for this address?”&lt;/p&gt;

&lt;h2&gt;
  
  
  A dashboard turns reads into something useful
&lt;/h2&gt;

&lt;p&gt;The first two reads happened in scripts. That is a good way to learn the API, but it is not how most users experience software.&lt;/p&gt;

&lt;p&gt;So Arc 2 moved the same read patterns into a small browser dashboard.&lt;/p&gt;

&lt;p&gt;The dashboard took an address, fetched its balance, pulled recent transactions, and displayed the results in a simple web interface. Same Solana calls. Different surface.&lt;/p&gt;

&lt;p&gt;That is a useful moment because it shows how normal this can feel.&lt;/p&gt;

&lt;p&gt;You are still doing familiar frontend work: input fields, loading states, error handling, formatted output. The Solana-specific part is the data source.&lt;/p&gt;

&lt;p&gt;A read-only dashboard is also a good early blockchain app because the risk is low. You are not signing anything. You are not moving funds. You are not changing state. You are just helping someone understand public data.&lt;/p&gt;

&lt;p&gt;That makes Arc 2 a good bridge for Web2 developers. You can use existing frontend instincts while slowly replacing “my backend API” with “the Solana RPC endpoint.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Accounts are not tables, but the database analogy helps
&lt;/h2&gt;

&lt;p&gt;The conceptual center of Arc 2 was the comparison between Solana accounts and Web2 databases.&lt;/p&gt;

&lt;p&gt;The database analogy helps because it gives you a place to start. Solana stores state. You can read that state. Programs interact with that state. Accounts have owners. Data has structure.&lt;/p&gt;

&lt;p&gt;But the analogy breaks quickly.&lt;/p&gt;

&lt;p&gt;In a normal database, you might expect tables, schemas, indexes, joins, private rows, server-side filtering, and application middleware enforcing access control.&lt;/p&gt;

&lt;p&gt;Solana does not work like that.&lt;/p&gt;

&lt;p&gt;On Solana, everything is an account: wallets, programs, token accounts, program-owned data. Accounts do not query each other. There are no joins. You usually assemble what you need off-chain by making RPC calls, reading account data, and interpreting the results in your app.&lt;/p&gt;

&lt;p&gt;That is a major shift.&lt;/p&gt;

&lt;p&gt;In Web2, the database often sits behind your application. Your server decides what to query, what to hide, what to join, and what to return.&lt;/p&gt;

&lt;p&gt;On Solana, the data is public, the runtime enforces ownership and write rules, and your application often becomes the layer that makes raw public state usable.&lt;/p&gt;

&lt;p&gt;That is why “Solana is a public database” is helpful, but incomplete. It is not Postgres with a blockchain logo. It is a shared state layer with different rules about visibility, ownership, storage, and writes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage has a price tag
&lt;/h2&gt;

&lt;p&gt;Arc 2 also introduced a detail Web2 developers do not usually think about directly: storage cost.&lt;/p&gt;

&lt;p&gt;In most Web2 apps, storage cost is real, but abstracted. You pay a cloud bill. Your database grows. Maybe you worry about indexes, backups, or object storage. But individual records do not usually arrive with an obvious upfront storage deposit.&lt;/p&gt;

&lt;p&gt;Solana makes storage more explicit.&lt;/p&gt;

&lt;p&gt;Accounts need enough lamports to be rent-exempt. That deposit depends on the amount of data the account stores, and it can be returned if the account is closed.&lt;/p&gt;

&lt;p&gt;That is a very different mental model from “just insert another row.”&lt;/p&gt;

&lt;p&gt;It means storage is not just a backend implementation detail. It is part of the application model. If your app needs on-chain state, someone has to pay for the account space that holds it.&lt;/p&gt;

&lt;p&gt;Arc 2 did not require us to build with that model yet, but it planted the idea early: on Solana, reading is free, but storing state is not invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Devnet and mainnet are separate worlds
&lt;/h2&gt;

&lt;p&gt;Arc 2 also made the network environment visible.&lt;/p&gt;

&lt;p&gt;In Web2, developers are used to staging and production. Same code, different database. Same API shape, different base URL. Different data, different risk.&lt;/p&gt;

&lt;p&gt;Solana has a similar idea with devnet and mainnet.&lt;/p&gt;

&lt;p&gt;The RPC calls can look identical. The address can be the same. But the network you query changes everything.&lt;/p&gt;

&lt;p&gt;A wallet might have devnet SOL and no mainnet SOL. It might have activity on one network and no history on the other. Devnet is not a sandboxed view of mainnet. It is a separate environment with separate data.&lt;/p&gt;

&lt;p&gt;That is a useful Web2 bridge:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Changing networks is like pointing the same app at a different database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The code shape stays familiar. The data source changes. The consequences change too.&lt;/p&gt;

&lt;p&gt;Devnet is where you can experiment freely. Mainnet is where the real assets and real history live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing turns learning into understanding
&lt;/h2&gt;

&lt;p&gt;Arc 2 ended by stepping away from the code and asking us to explain what we had learned.&lt;/p&gt;

&lt;p&gt;That was not filler. Writing is part of the learning loop.&lt;/p&gt;

&lt;p&gt;When you try to explain Solana accounts, RPC calls, devnet, public data, or transaction history to someone else, you quickly find the gaps in your own understanding. The rough edges become visible. The parts that felt “sort of clear” either sharpen or fall apart.&lt;/p&gt;

&lt;p&gt;That is valuable.&lt;/p&gt;

&lt;p&gt;A good technical learning journey is not just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read docs → copy code → move on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try something → build something → explain it → share it → notice what still feels unclear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arc 2 followed that pattern. We read on-chain data, turned it into a browser dashboard, compared it against Web2 databases, wrote about what clicked, and shared the work publicly.&lt;/p&gt;

&lt;p&gt;That turns a week of exercises into something more durable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 2 sets up
&lt;/h2&gt;

&lt;p&gt;Strip Arc 2 back to its core and the main ideas are clear:&lt;/p&gt;

&lt;p&gt;Solana data is public by default. You read it through RPC calls. Wallet balances, transaction history, programs, and data all live in accounts. Devnet and mainnet are separate environments. Reading is free, but storing state has a cost.&lt;/p&gt;

&lt;p&gt;That gives us the bridge into Arc 3.&lt;/p&gt;

&lt;p&gt;Once you understand how to read from Solana, the next question is obvious:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do you change it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On Solana, the answer is transactions.&lt;/p&gt;

&lt;p&gt;Arc 3 picks up there: signed requests to change on-chain state, SOL transfers, transaction anatomy, confirmation, failure modes, and the write path developers use to interact with the network.&lt;/p&gt;

&lt;p&gt;Use this post as the map, revisit the Arc 2 challenges when you want the hands-on version, and jump into Arc 3 with the read model already in place.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>learning</category>
    </item>
    <item>
      <title>Fungible and Non-Fungible Tokens on Solana: Same System, Different Rules</title>
      <dc:creator>Vincent Jande</dc:creator>
      <pubDate>Fri, 29 May 2026 18:40:51 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/fungible-and-non-fungible-tokens-on-solana-same-system-different-rules-3h3</link>
      <guid>https://dev.to/100daysofsolana/fungible-and-non-fungible-tokens-on-solana-same-system-different-rules-3h3</guid>
      <description>&lt;p&gt;If you have been following the 100 Days of Solana challenges, you have already worked with tokens. You created mints, set up token accounts, transferred SOL, and explored token extensions. But there is a distinction that comes up constantly in web3, and understanding it properly will change how you think about everything you build going forward.&lt;/p&gt;

&lt;p&gt;Fungible and non-fungible tokens. You have probably heard these terms before, especially NFTs. But what do they actually mean on Solana, and how does the same token system handle two very different concepts?&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes something fungible
&lt;/h2&gt;

&lt;p&gt;Fungible just means interchangeable. One unit is identical to another unit. If I have 10 USDC and you have 10 USDC, ours are exactly the same. It does not matter which specific USDC tokens I hold because they are all worth the same and behave the same way. We could swap them and nothing changes.&lt;/p&gt;

&lt;p&gt;This is how most things you are used to work. A dollar bill is fungible. A liter of petrol is fungible. One unit of SOL is the same as any other unit of SOL. When you built token transfers in the challenges, you were working with fungible tokens. You did not need to care about which specific tokens moved, just how many.&lt;/p&gt;

&lt;p&gt;Fungible tokens are used for currencies, stablecoins, utility tokens, governance tokens, loyalty points, in-game currencies, and anything where the quantity matters more than the individual unit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes something non-fungible
&lt;/h2&gt;

&lt;p&gt;Non-fungible means unique. Each token is different from every other token, even if they come from the same collection. If I have NFT #42 from a collection and you have NFT #87, those are not interchangeable. They might have different images, different properties, different rarity, or different utility.&lt;/p&gt;

&lt;p&gt;Think of it like event tickets. Two tickets to the same concert are not the same if one is front row and the other is in the back. They came from the same event, but each one is distinct.&lt;/p&gt;

&lt;p&gt;Non-fungible tokens are used for digital art, collectibles, membership passes, certifications, domain names, gaming items, real estate deeds, event tickets, and anything where the individual item matters more than the quantity.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Solana handles both with the same system
&lt;/h2&gt;

&lt;p&gt;This is where Solana does something interesting. On some blockchains, fungible and non-fungible tokens are completely separate systems with different standards and different smart contracts. On Solana, both run through the same Token Program (or the newer Token Extensions program, also called Token-2022).&lt;/p&gt;

&lt;p&gt;The difference comes down to two configuration settings: decimals and supply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fungible token has a mint initialized with multiple decimals (typically 6 or 9) and a supply far greater than one. You can mint millions of fractional units from the same mint, and they are all interchangeable.&lt;/li&gt;
&lt;li&gt;A non-fungible token is a mint initialized with exactly 0 decimals, where exactly one token is minted and the mint authority is then permanently revoked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That second part is worth being precise about, because there is no "maximum supply" field you set when you create a base SPL mint. A mint has a supply value that simply increments every time you mint into it. You create the mint with 0 decimals, mint exactly one token to a token account, and then set the mint authority to None. Revoking the authority is what locks the total supply at 1 forever, guaranteeing that no more units of this specific token can ever be printed.&lt;/p&gt;

&lt;p&gt;Same program, same mint account layout. The rules are enforced purely by how you configure the mint and what you do with the mint authority.&lt;/p&gt;

&lt;p&gt;One nuance: at this level you have created a non-fungible token. Wallets and marketplaces do not yet recognize it as an "NFT" in the way you would see it on a marketplace. That recognition comes from the metadata and standard layer, which is what we cover next.&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%2Fi63bstmcy97uinwg7wgv.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%2Fi63bstmcy97uinwg7wgv.png" alt=" Left panel " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The role of metadata
&lt;/h2&gt;

&lt;p&gt;If a non-fungible token is just a mint with a supply of one, how do you know what it actually represents? How do you attach an image, a name, or specific traits to it?&lt;/p&gt;

&lt;p&gt;This is where metadata comes in. On-chain, a token mint is just a state account holding configuration details and balances. The metadata gives it context.&lt;/p&gt;

&lt;p&gt;The long-standing approach is the Metaplex Token Metadata standard, which creates a separate metadata account cryptographically linked to the token mint via a Program Derived Address (PDA). This account holds the token's name, symbol, and a URI pointing to an off-chain JSON file containing the image and attributes. That JSON is typically stored on decentralized storage like Arweave or IPFS, though some projects use centralized hosting such as AWS S3.&lt;/p&gt;

&lt;p&gt;Token Metadata is still widely used and is not deprecated. Interestingly, a large share of assets minted through it today are actually fungible tokens, since it is also the common way to give a fungible mint a name, symbol, and icon.&lt;/p&gt;

&lt;p&gt;For new NFT projects specifically, the current recommended standard is &lt;strong&gt;Metaplex Core&lt;/strong&gt;. Core uses a single-account design, storing all of an asset's data in one account instead of the separate mint, metadata, and token accounts the older model required. That cuts minting cost dramatically (on the order of 80 percent cheaper than Token Metadata) and ships with built-in features like enforced royalties and a plugin system for custom behavior. If you are starting a new NFT collection today, Core is usually where you should look first.&lt;/p&gt;

&lt;p&gt;There is also the &lt;strong&gt;Token Extensions (Token-2022)&lt;/strong&gt; path. By enabling the MetadataPointer and TokenMetadata extensions, you can store the name, symbol, and URI directly inside the mint account itself. The pointer says where the metadata lives, and the metadata extension holds the actual fields. In practice this is used most often for fungible and utility tokens, letting a stablecoin or reward token carry a native icon and tracking data without a separate metadata account or any cross-program coordination at creation.&lt;/p&gt;

&lt;p&gt;So the honest map of the landscape is: Token Metadata (legacy, still common, heavily used for fungibles), Core (the modern recommendation for new NFTs), Token-2022 metadata extensions (great for fungible and utility tokens), and compressed NFTs for high-volume cases, which we will get to in a moment.&lt;/p&gt;

&lt;p&gt;For fungible tokens, metadata is simple. You usually just need a name, symbol, and maybe an icon. For non-fungible tokens, metadata can be extensive because each token is unique and needs its own description, image, and properties.&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%2F9kyia5kpp557f458zf5y.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%2F9kyia5kpp557f458zf5y.png" alt="A horizontal flow: Token Account (balance: 1) → Mint Account → Metadata, where Metadata branches into two stacked boxes labeled " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Token accounts work the same way
&lt;/h2&gt;

&lt;p&gt;Whether you are holding a fungible token or a non-fungible one, you need a token account for it. This is the same concept you learned in the challenges. Your wallet needs an Associated Token Account (ATA) for each mint you want to hold.&lt;/p&gt;

&lt;p&gt;For fungible tokens, your token account has a balance that can go up and down as you send and receive tokens. For non-fungible tokens, your token account has a balance of exactly one. You either have it or you do not.&lt;/p&gt;

&lt;p&gt;The mechanics are identical. Creating the account, paying rent, transferring tokens. The same code patterns you have been writing in the challenges apply to both. (Metaplex Core is the one exception worth noting, since its single-account model does not use a separate token account in the same way, but the fungible side and classic NFTs both follow the familiar ATA pattern.)&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%2Fi5vo1woaf0xgovkuufuh.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%2Fi5vo1woaf0xgovkuufuh.png" alt="Digital asset spectrum, ranging from pure fungible tokens to unique non-fungible assets," width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Editions, collections, and scale
&lt;/h2&gt;

&lt;p&gt;Not every NFT is a complete one-of-one. Sometimes you want to create multiple copies of the same item but still have each one be individually trackable. Think limited edition prints. There are 500 of them, each one is numbered, but they all share the same artwork.&lt;/p&gt;

&lt;p&gt;In the Token Metadata standard, this is handled through editions. A master edition can produce a set number of prints. Each print is its own mint with its own token, but they are all linked back to the original master edition.&lt;/p&gt;

&lt;p&gt;Collections group related NFTs together. A collection is itself an NFT that acts as a parent. Individual NFTs reference the collection in their metadata, which is how marketplaces know that 10,000 different mints all belong to the same project.&lt;/p&gt;

&lt;p&gt;Here is an important practical point that trips people up. Editions and one-mint-per-item NFTs do not scale cheaply. Each one is a real account that carries rent. So if your goal is something like 1,000 identical-but-individually-owned event tickets, minting a thousand separate full NFTs gets expensive fast.&lt;/p&gt;

&lt;p&gt;For that high-volume case, the modern answer is &lt;strong&gt;compressed NFTs&lt;/strong&gt; (the Metaplex Bubblegum standard). Compressed NFTs store ownership data in a Merkle tree rather than in a full account per token, which makes it possible to mint thousands or even millions of individually owned, transferable assets at a tiny fraction of the cost. This is the dominant approach for large drops on Solana today, and it is exactly what you would reach for in the 1,000-ticket scenario.&lt;/p&gt;

&lt;p&gt;So the decision for "many of the same thing" is really: a handful of numbered prints can use editions, but anything at real scale should use compression.&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%2Fdzerb3a04x0nn7dxl89s.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%2Fdzerb3a04x0nn7dxl89s.png" alt="A three-column comparison (table or cards): Token Metadata vs Core vs Bubblegum (compressed). Rows for " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fungible tokens with NFT properties
&lt;/h2&gt;

&lt;p&gt;The Token Extensions program blurs the traditional line between fungible and non-fungible tokens in powerful ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Non-Transferable Tokens&lt;/strong&gt;: By initializing a fungible mint with the NonTransferable extension, you create assets that are locked to the recipient's wallet the moment they are minted. If a user earns 50 reputation points, those points are structurally fungible but behave like "soulbound" credentials because the runtime blocks any transfer instruction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mint-Linked Metadata&lt;/strong&gt;: Before Token Extensions, detailed metadata was largely reserved for NFTs via Metaplex. Now, you can inject rich metadata directly into a fungible utility token or stablecoin mint, allowing wallets to display custom icons and tracking data natively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transfer Hooks&lt;/strong&gt;: You can attach a custom program to a fungible token mint that executes additional logic every time a transfer occurs. A fungible token that checks an allowlist or verifies a KYC credential before allowing a transfer begins to behave like a restricted real-world security, changing its economic behavior entirely through code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The categories are not as rigid as they seem. Solana gives you the building blocks and you decide how to combine them.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use which
&lt;/h2&gt;

&lt;p&gt;If you are building something where every unit is the same and users care about quantity, use a fungible token. Currencies, points, credits, governance votes.&lt;/p&gt;

&lt;p&gt;If you are building something where each item is unique and users care about the specific item they hold, use a non-fungible token. Art, collectibles, certificates, tickets, identity documents. For a brand-new collection, start with Metaplex Core. For high-volume drops, reach for compressed NFTs.&lt;/p&gt;

&lt;p&gt;If you are building something in between, like a limited-edition with 100 copies or a membership tier with different levels, look at editions and collections, or explore how Token-2022 extensions can give you the behavior you need.&lt;/p&gt;

&lt;p&gt;The good news is that the skills you are building in these challenges apply to all of it. The mint, token account, and transfer patterns are the same regardless of whether you are working with fungible or non-fungible tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is coming next
&lt;/h2&gt;

&lt;p&gt;The upcoming challenges will take you deeper into how NFTs work on Solana, how to mint them, how metadata is structured, and how collections come together. Understanding the foundation covered here will make those challenges click faster.&lt;/p&gt;

&lt;p&gt;Keep building. See you in the Discord.&lt;/p&gt;

&lt;p&gt;100 Days of Solana is a free daily coding challenge: &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;https://mlh.link/solana-100&lt;/a&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Web3 Apps You Can Build With Token Extensions</title>
      <dc:creator>Vincent Jande</dc:creator>
      <pubDate>Fri, 22 May 2026 20:07:47 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/web3-apps-you-can-build-with-token-extensions-me8</link>
      <guid>https://dev.to/100daysofsolana/web3-apps-you-can-build-with-token-extensions-me8</guid>
      <description>&lt;p&gt;In our previous articles, we covered &lt;a href="https://dev.to/100daysofsolana/how-web3-tokens-get-their-value-3bbk"&gt;how Web3 tokens get their value&lt;/a&gt; and broke down the new standards in &lt;a href="https://dev.to/100daysofsolana/what-is-token-2022-and-why-solana-built-it-53io"&gt;What is Token 2022 and why Solana built it&lt;/a&gt;. You understand authorities, token accounts, and the extensions available.&lt;/p&gt;

&lt;p&gt;Now the question is what you actually build.&lt;/p&gt;

&lt;p&gt;Token extensions are building blocks for real products. Each extension solves a specific problem. By leveraging Token 2022 via modern client libraries, you can build applications from frontend or backend scripts that would have required custom smart contracts before.&lt;/p&gt;

&lt;p&gt;Here are some app ideas, the extensions they use, and the tools to start building them.&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%2F6ugcjs97lcplom3rb7xp.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%2F6ugcjs97lcplom3rb7xp.png" alt="A row of simple glowing tokens on the left flowing through thin light streams into a larger central platform with translucent floating cards arranged around it." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Creator Subscription Platform
&lt;/h2&gt;

&lt;p&gt;Subscribers hold a token that grants monthly access to exclusive content. The token cannot be resold or moved between wallets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensions&lt;/strong&gt;: Non-transferable tokens prevent resale; metadata stores the creator name, tier, and subscription period.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;: Create one mint per creator per subscription period, one for "Creator X - January 2026," another for February. When a user subscribes, bundle a USDC payment with a non-transferable token mint into a single atomic transaction.&lt;/p&gt;

&lt;p&gt;Use one mint per period because Token-2022 metadata is mint-level, every holder of a given mint reads the same expiry. Gating access is then a direct read of the period from the mint's on-chain metadata against the current date.&lt;/p&gt;

&lt;p&gt;To reclaim or burn subscription tokens directly, add PermanentDelegate at mint creation. The extension itself can never be removed once the mint is initialized, though the delegate authority can be rotated or set to None via SetAuthority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt;: @solana/kit, @solana-program/token-2022, Next.js or React.&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%2Fshg79r2jwx3fv3s1xqod.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%2Fshg79r2jwx3fv3s1xqod.png" alt="Translucent token" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Loyalty Rewards Program
&lt;/h2&gt;

&lt;p&gt;A business issues loyalty points as tokens. Customers earn points on purchases and redeem them for discounts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensions&lt;/strong&gt;: Non-transferable tokens keep points tied to the customer; the business retains mint authority to issue new points; metadata displays the brand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;: Each business gets its own token mint. The backend mints points directly into the customer's token account on purchase events. Customers connect their wallet to a redemption portal to spend points.&lt;/p&gt;

&lt;p&gt;On-chain identity here is pseudonymous, not private, wallet balances and minting activity are visible to anyone. Non-transferability stops third parties from trading or seizing the points, but doesn't hide them from view.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt;: @solana/kit, @solana-program/token-2022, Node.js, React, Solana Pay.&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%2F7qmoyorl0g3rsnjm4fl3.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%2F7qmoyorl0g3rsnjm4fl3.png" alt="A glowing storefront-shaped block with small luminous tokens streaming out toward a translucent wallet card" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Regulated Investment Token
&lt;/h2&gt;

&lt;p&gt;A real estate company tokenizes a property. Only verified investors can hold the token, and the company needs a way to freeze accounts and recover tokens if compliance requires it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensions&lt;/strong&gt;: Default account state can be set to frozen so new holders start out locked until KYC clears. Freeze authority handles ongoing compliance actions, like freezing an account that falls out of good standing. A permanent delegate provides a recovery path for legal scenarios. Metadata stores property details and links to legal documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;: New investor token accounts start frozen and are thawed after the investor passes KYC. The freeze authority can re-freeze any account later if compliance requires it, and the permanent delegate handles court-ordered recovery if it ever comes to that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt;: @solana/kit and @solana-program/token-2022 for the mint, freeze actions, and delegate operations, plus a KYC provider API for investor verification.&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%2Fviovtbjzixdluf7jvfyt.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%2Fviovtbjzixdluf7jvfyt.png" alt="A translucent building-shaped block split into glowing fragments, with a faint shield outline in front and a small padlock symbol floating beside it." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. In-Game Currency with Transaction Fees
&lt;/h2&gt;

&lt;p&gt;Players earn an in-game currency through gameplay and trade it with each other. The studio automatically collects a small fee on every user-to-user transfer to generate ongoing revenue from the game economy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensions&lt;/strong&gt;: Transfer fees collect a percentage on every token movement; metadata handles branding like names, icons, and descriptions; mint authority is retained so the studio can inject new currency into the economy via gameplay achievements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;: Fees are configured at mint creation and collected automatically on token transfers with no custom program or market smart contract required. When players send tokens to each other, the configured fee is withheld directly inside the recipient's token account.&lt;/p&gt;

&lt;p&gt;Because fees accumulate quietly inside thousands of individual player accounts, the studio's withdraw-withheld authority can periodically collect these withheld amounts and pool them into a central treasury wallet, without touching player balances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt;: @solana/kit, @solana-program/token-2022, a game client SDK, and an administrative backend.&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%2Fr7yc59n7a1qxo461hhtm.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%2Fr7yc59n7a1qxo461hhtm.png" alt="Two translucent player avatars facing each other with glowing tokens flowing between them, small droplets falling into a luminous chest below." width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Patterns to Notice
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;@solana-program/token-2022 is often all you need&lt;/strong&gt;. For loyalty, subscriptions, in-game currencies, and yield-bearing wrappers, you can usually compose instructions against it from @solana/kit and skip Rust. Reach for Anchor when you need to build custom programs or manage complex state logic that standard extensions don't cover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan compatibility before mint creation&lt;/strong&gt;. Not every extension pairs with every other extension. Some combinations are blocked because their behaviors conflict (for example, anything that gates or modifies transfers tends to clash with non-transferability). Most extensions can't be added after mint creation, so check the compatibility matrix in the Token-2022 docs before committing to a design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"No Rust required" is not "no centralized authority required."&lt;/strong&gt; Permanent delegate, freeze authority, mint authority, and withdraw-withheld authority are centralized controls encoded in extension config. If any are live on your token, make sure to state them clearly in your project's docs or UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start thinking about what you want to build
&lt;/h2&gt;

&lt;p&gt;You have the fundamentals. The app ideas above are starting points, take one, modify it, make it your own.&lt;/p&gt;

&lt;p&gt;Keep building. See you on Discord.&lt;/p&gt;

&lt;p&gt;100 Days of Solana is a free daily coding challenge: &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;https://mlh.link/solana-100&lt;/a&gt;&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>$500 Challenge Drop</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Fri, 22 May 2026 14:54:18 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/500-challenge-drop-o9</link>
      <guid>https://dev.to/100daysofsolana/500-challenge-drop-o9</guid>
      <description>&lt;p&gt;If you’re taking part in &lt;strong&gt;100 Days of Solana&lt;/strong&gt;, look out for the  &lt;strong&gt;$500 Challenge Drop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Complete &lt;strong&gt;Monday’s or Tuesday’s challenge&lt;/strong&gt; (Day 36 or 37) and you’ll be entered for a random chance to win &lt;strong&gt;$500&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s it: complete the challenge, submit your work, and you’re in the draw.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is 100 Days of Solana?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;100 Days of Solana&lt;/strong&gt; is a daily, hands-on learning challenge from MLH and Solana for Web2 developers who want to understand Solana by building with it.&lt;/p&gt;

&lt;p&gt;Each challenge introduces one practical concept at a time, using familiar JavaScript tooling while gradually moving from fundamentals like keypairs and transactions into tokens, accounts, and programmable assets.&lt;/p&gt;

&lt;p&gt;This week’s challenges continue the move into &lt;strong&gt;Token Extensions&lt;/strong&gt;, where Solana tokens start to feel less like static assets and more like programmable building blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to enter
&lt;/h2&gt;

&lt;p&gt;To be entered for the &lt;strong&gt;$500 Challenge Drop&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Complete &lt;strong&gt;Monday’s or Tuesday’s 100 Days of Solana challenge&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Submit your work at the bottom of the challenge&lt;/li&gt;
&lt;li&gt;You’ll be entered for a random chance to win &lt;strong&gt;$500&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start here: &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;mlh.link/solana-100&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Complete the challenge, submit your work, and you’re in.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Arc 1 Recap: Keypairs, Wallets, and Solana Fundamentals</title>
      <dc:creator>Matthew Revell</dc:creator>
      <pubDate>Fri, 22 May 2026 10:16:13 +0000</pubDate>
      <link>https://dev.to/100daysofsolana/arc-1-recap-keypairs-wallets-and-solana-fundamentals-2hkj</link>
      <guid>https://dev.to/100daysofsolana/arc-1-recap-keypairs-wallets-and-solana-fundamentals-2hkj</guid>
      <description>&lt;p&gt;Typical web and mobile development often starts with a few familiar questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's the data model going to look like?&lt;/li&gt;
&lt;li&gt;How am I going to handle user accounts and auth?&lt;/li&gt;
&lt;li&gt;Where am I going to host this thing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Web3 development has its own set of "how do I start?" questions but they're not the same as what you'd expect if you're coming from a Web2 background.&lt;/p&gt;

&lt;p&gt;Arc 1 of &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana&lt;/a&gt; was all about learning those fundamentals.&lt;/p&gt;

&lt;p&gt;What are the Solana equivalents of user accounts, dev/prod environments, data storage, and so on?&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity starts with a keypair
&lt;/h2&gt;

&lt;p&gt;Identify works differently in the Web3 world. It starts with a keypair, rather than an account someone creates for you.&lt;/p&gt;

&lt;p&gt;But the similarities with typical user accounts break down once you look at the detail.&lt;/p&gt;

&lt;p&gt;A Solana keypair is created on your own computer.&lt;/p&gt;

&lt;p&gt;There is no signup request, no account creation API, and no backend service issuing you an identity. Your machine generates two linked pieces of data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a public key &lt;/li&gt;
&lt;li&gt;a private key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The public key is your Solana address. You can share it freely, paste it into a faucet, or look it up in a block explorer.&lt;/p&gt;

&lt;p&gt;The private key proves that you control that address. You keep it secret, just like you would keep an SSH private key secret.&lt;/p&gt;

&lt;p&gt;The important thing to understand is this:&lt;/p&gt;

&lt;p&gt;Creating a keypair gives you a valid Solana address, but it does not automatically create anything on-chain.&lt;/p&gt;

&lt;p&gt;That can feel strange if you are coming from Web2. In a normal web app, a user account usually appears only after a row is inserted into a database. On Solana, the address can exist before the network has stored any data for it.&lt;/p&gt;

&lt;p&gt;So there are two related ideas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The address:&lt;/strong&gt;&lt;br&gt;
This exists as soon as your keypair exists. It is just derived from your public key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The on-chain account:&lt;/strong&gt;&lt;br&gt;
This exists on a specific Solana cluster only once the network starts storing state for that address. For example, that might happen when you receive devnet SOL from a faucet.&lt;/p&gt;

&lt;p&gt;That means a brand-new address can be completely valid, even if Solana Explorer shows no account data for it yet.&lt;/p&gt;

&lt;p&gt;It is a bit like generating an SSH key. You can create the key locally before any server knows about it. The public key is real immediately, but it only becomes useful on a specific server once that server has been told about it. On Solana, your address is real immediately, but a particular cluster only stores account state for it once something happens there.&lt;/p&gt;

&lt;p&gt;This is one of the first places where the usual Web2 mental model starts to shift. In a Web2 app, an account usually begins as something a service creates and stores. On Solana, control is cryptographic: you control the wallet because you hold the key that can sign for it. No company needs to store your credentials before the address exists, and no admin panel can recover the private key for you.&lt;/p&gt;

&lt;p&gt;That is not the whole story of Solana, but it is one of the foundations everything else builds on. If you do not understand what controls an address, transactions, accounts, tokens, and programs all feel more mysterious than they need to.&lt;/p&gt;

&lt;h2&gt;
  
  
  From generated key to funded address
&lt;/h2&gt;

&lt;p&gt;If you know one thing about Web3, it might be that each blockchain has its own currency. On Solana, that currency is SOL.&lt;/p&gt;

&lt;p&gt;SOL is what pays for activity on the network. In Arc 1, though, we did not start by spending real money. We started with devnet SOL: free test tokens used on Solana’s developer network.&lt;/p&gt;

&lt;p&gt;A few lines of @solana/kit produced a brand-new keypair. The important call was small: generateKeyPairSigner() created a signer and returned an address that we could print straight to the terminal.&lt;/p&gt;

&lt;p&gt;That first step is intentionally plain. Before there is a wallet app, login screen, dashboard, or account setup flow, there is just a keypair:&lt;/p&gt;

&lt;p&gt;a public key, which gives you a Solana address&lt;br&gt;
a private key, which proves you control that address&lt;/p&gt;

&lt;p&gt;We then funded that address with free devnet SOL from the Solana faucet. More precisely, the faucet sent lamports to the address. Lamports are the smallest unit of SOL, a bit like cents are to dollars, except 1 SOL equals 1,000,000,000 lamports.&lt;/p&gt;

&lt;p&gt;After that, a balance check showed that the address now had account state on devnet.&lt;/p&gt;

&lt;p&gt;That is a useful break from the usual Web2 sequence.&lt;/p&gt;

&lt;p&gt;In a typical web app, the user account usually starts as a row in a database. The app creates the account, stores the user record, and then the user can do things.&lt;/p&gt;

&lt;p&gt;On Solana, the address can exist first. It is valid as soon as the keypair is generated. The network only starts storing account state for that address on a particular cluster once something happens there, such as receiving lamports.&lt;/p&gt;

&lt;p&gt;Arc 1 also introduced devnet, one of the most important environments for learning Solana. We were not using local mocks or screenshots of a production system. We were using a real Solana cluster with the same core APIs and concepts as mainnet, but with tokens that have no real-world value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistence makes the key useful
&lt;/h2&gt;

&lt;p&gt;Generating a keypair is only the first step.&lt;/p&gt;

&lt;p&gt;If the keypair lives only in memory, it disappears when the script exits. That is fine for a quick demo, but it is not enough if we want to keep using the same Solana identity.&lt;/p&gt;

&lt;p&gt;So the next step was persistence. We saved the keypair to a local JSON file, then loaded it again each time the script ran.&lt;/p&gt;

&lt;p&gt;That gave us the same address across multiple runs.&lt;/p&gt;

&lt;p&gt;This is the point where Solana identity starts to feel less abstract. In a Web2 app, your identity is usually tied to something recoverable: an email address, a password reset flow, maybe a login provider.&lt;/p&gt;

&lt;p&gt;With a keypair, the private key is the thing that matters.&lt;/p&gt;

&lt;p&gt;If someone has the private key, they can sign transactions for that address. They do not need your password. They do not need your email account. They do not need approval from a backend service.&lt;/p&gt;

&lt;p&gt;For devnet, storing a keypair in a local JSON file is fine. There is no real value at stake. But the same approach would be a serious mistake for mainnet funds or production users.&lt;/p&gt;

&lt;p&gt;Arc 1 made that visible early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key storage affects security.&lt;/li&gt;
&lt;li&gt;Address reuse affects privacy.&lt;/li&gt;
&lt;li&gt;Signing is how authorization works.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lamports make value precise
&lt;/h2&gt;

&lt;p&gt;Arc 1 also introduced the unit Solana programs actually work with: lamports.&lt;/p&gt;

&lt;p&gt;Wallets usually show balances in SOL because that is easier for humans to read. But Solana code works in lamports because programs need exact whole-number values.&lt;/p&gt;

&lt;p&gt;One SOL is 1,000,000,000 lamports.&lt;/p&gt;

&lt;p&gt;This should feel familiar if you have worked with payments APIs. Stripe does not ask you to send $19.99 as a decimal amount. It asks for 1999 cents, because money should not be handled with floating point arithmetic.&lt;/p&gt;

&lt;p&gt;Solana follows the same basic idea. The network needs every validator to calculate the same result exactly. That means balances, transfers, and fees are represented as integers, not rounded decimal values.&lt;/p&gt;

&lt;p&gt;So there are two units to keep straight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SOL is the readable display unit.&lt;/li&gt;
&lt;li&gt;Lamports are the exact unit used by the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That small distinction teaches a bigger Solana lesson. What humans see is not always what programs store, sign, or verify. As a developer, you need to know when you are dealing with a friendly display value and when you are dealing with the precise value the network will actually use.&lt;/p&gt;

&lt;h2&gt;
  
  
  The app gets an address, not your private key
&lt;/h2&gt;

&lt;p&gt;After working with raw keypairs in scripts, we connected a real browser wallet.&lt;/p&gt;

&lt;p&gt;Using the Wallet Standard and @wallet-standard/app, we built a browser app that could detect installed wallets such as Phantom, Solflare, and Backpack. The app requested a connection, then displayed the selected address and its devnet balance.&lt;/p&gt;

&lt;p&gt;That is the important distinction: the app could show the address and read the balance, but it never handled the private key.&lt;/p&gt;

&lt;p&gt;The wallet kept custody of the key. The app only received the public address.&lt;/p&gt;

&lt;p&gt;For Web2 developers, this is closer to using an external identity provider than asking users to type their password into every app. The app delegates key management to the wallet. When it needs proof that the user controls an address, it asks the wallet to sign something.&lt;/p&gt;

&lt;p&gt;But connecting a wallet is not the same as a full login session.&lt;/p&gt;

&lt;p&gt;A wallet connection gives the app an address. It tells the app, “This is the address the user has chosen to share.” It does not automatically prove that the user should stay logged in, access a private account, or perform sensitive actions.&lt;/p&gt;

&lt;p&gt;For that, an app usually needs a separate signing step. The wallet signs a message or transaction, and that signature proves control of the address. The wallet should ask the user before signing anything meaningful.&lt;/p&gt;

&lt;p&gt;That is why real Solana apps should not ask users to paste secret keys into a form. The private key stays in the wallet. The app asks the wallet for addresses and signatures.&lt;/p&gt;

&lt;p&gt;Arc 1 covered both raw keypair management and browser wallet connection because they teach different parts of the same model:&lt;/p&gt;

&lt;p&gt;Raw keypairs show what is happening underneath.&lt;br&gt;
Wallet connections show how users should normally interact with apps.&lt;/p&gt;

&lt;p&gt;If you come from Web2, connecting a wallet can look a bit like “Sign in with Google.”&lt;/p&gt;

&lt;p&gt;Your app does not handle the user’s Google password. The user approves access through Google, and your app receives enough information to recognize them.&lt;/p&gt;

&lt;p&gt;A Solana wallet plays a similar role in the app experience. It holds the user’s keys, asks for approval, and gives the app a public address to work with.&lt;/p&gt;

&lt;p&gt;But the comparison only gets you so far.&lt;/p&gt;

&lt;p&gt;Google is an identity provider. It can reset access, suspend accounts, enforce policies, and sit between the user and the apps they use.&lt;/p&gt;

&lt;p&gt;A non-custodial wallet is different. It manages keys and asks the user to approve signatures, but it cannot recreate a lost private key. It also cannot invalidate a private key that still exists somewhere else.&lt;/p&gt;

&lt;p&gt;That changes the shape of identity.&lt;/p&gt;

&lt;p&gt;In a typical Web2 app, identity often starts with a database row. The app creates a user account, stores profile data, and provides recovery flows if something goes wrong.&lt;/p&gt;

&lt;p&gt;On Solana, the address is the durable identifier. A signature is the proof that someone controls that address.&lt;/p&gt;

&lt;p&gt;Your app can associate data with an address, but it does not own the user’s identity.&lt;/p&gt;

&lt;p&gt;That can feel like losing control if you are used to backend-owned accounts. But it is one of the tradeoffs Solana asks developers to understand. Users can bring the same address to different Solana apps. They can connect, approve, disconnect, and move on.&lt;/p&gt;

&lt;p&gt;That portability is powerful, but it has a privacy cost. If someone reuses the same address across many apps, their activity may be easier to link together.&lt;/p&gt;

&lt;p&gt;That is why the Web2 analogies in Arc 1 are useful, but only up to a point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH keys help explain keypairs.&lt;/li&gt;
&lt;li&gt;Payments APIs help explain lamports.&lt;/li&gt;
&lt;li&gt;OAuth-style login helps explain wallet connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those comparisons maps perfectly. But each gives Web2 developers a foothold before the Solana model starts to feel natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  There is no ordinary password reset
&lt;/h2&gt;

&lt;p&gt;Arc 1 also introduced one of the harder parts of the Solana mental model:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you lose the private key, there is no ordinary password reset.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In a typical Web2 app, the service controls the account system. That is why it can offer recovery flows. You can reset a password, verify an email address, contact support, or use an identity provider to get back in.&lt;/p&gt;

&lt;p&gt;That convenience comes with a tradeoff. The same service can also lock you out, leak credential data, change the rules, or shut the account down.&lt;/p&gt;

&lt;p&gt;Solana works differently.&lt;/p&gt;

&lt;p&gt;On Solana, control comes from signing. If you can sign with the private key for an address, you control that address. If you cannot sign, the network does not know that you are “really” the owner.&lt;/p&gt;

&lt;p&gt;That makes key storage a real product decision, not just a technical detail.&lt;/p&gt;

&lt;p&gt;Different approaches make different tradeoffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser wallets are convenient, but connected to everyday devices.&lt;/li&gt;
&lt;li&gt;Hardware wallets add protection, but introduce more friction.&lt;/li&gt;
&lt;li&gt;Custodial services can offer recovery, but someone else holds or manages the keys.&lt;/li&gt;
&lt;li&gt;Multisig tools can share control across multiple people or devices.&lt;/li&gt;
&lt;li&gt;Cold storage can improve security, but is less practical for frequent use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule for real apps is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not ask users to paste secret keys or seed phrases into your app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Solana app should connect to a wallet and ask for signatures. The wallet keeps the private key. The user approves or rejects the request. The app receives only what it needs.&lt;/p&gt;

&lt;p&gt;Recovery also has to be designed deliberately. It is not a default support flow you get for free. Seed phrases, hardware wallets, multisig setups, and custody providers all exist because key loss, key theft, and shared control are real product problems.&lt;/p&gt;

&lt;p&gt;Once that clicks, self-custody stops sounding like a slogan. It becomes a set of design choices about security, usability, and recovery.&lt;/p&gt;

&lt;p&gt;You are building around a different foundation: control is proven by signatures, not granted by a service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Arc 1 sets up
&lt;/h2&gt;

&lt;p&gt;By the end of Arc 1, we had worked through the core Solana fundamentals that everything else builds on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating a keypair in code&lt;/li&gt;
&lt;li&gt;understanding public keys and private keys&lt;/li&gt;
&lt;li&gt;funding an address on devnet&lt;/li&gt;
&lt;li&gt;seeing the difference between an address and account state&lt;/li&gt;
&lt;li&gt;saving and reloading a keypair from a local file&lt;/li&gt;
&lt;li&gt;converting between SOL and lamports&lt;/li&gt;
&lt;li&gt;reading raw lamport balances in logs&lt;/li&gt;
&lt;li&gt;connecting a browser wallet to a web app

&lt;ul&gt;
&lt;li&gt;understanding that connecting a wallet and signing are separate actions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;explaining Solana identity in our own words&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;For Web2 developers, some of this should feel familiar.&lt;/p&gt;

&lt;p&gt;If you have generated an SSH key, you already have a starting point for understanding keypairs. If you have used “Sign in with Google,” you already have a rough analogy for wallet connection. If you have handled money in cents through a payments API, you already understand why Solana uses lamports instead of decimal SOL values in code.&lt;/p&gt;

&lt;p&gt;None of those comparisons is perfect. But they give us useful footholds.&lt;/p&gt;

&lt;p&gt;Want to see the full thing? Check out the &lt;a href="https://mlh.link/solana-100" rel="noopener noreferrer"&gt;100 Days of Solana daily challenge series&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>100daysofsolana</category>
      <category>web3</category>
      <category>learning</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
