<?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: Ali Hamza</title>
    <description>The latest articles on DEV Community by Ali Hamza (@ali_hamza_4bd92ee9f67e78702).</description>
    <link>https://dev.to/ali_hamza_4bd92ee9f67e78702</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%2Fuser%2Fprofile_image%2F3948216%2F90fce051-5940-4da4-99d9-081b25cb6b31.png</url>
      <title>DEV Community: Ali Hamza</title>
      <link>https://dev.to/ali_hamza_4bd92ee9f67e78702</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ali_hamza_4bd92ee9f67e78702"/>
    <language>en</language>
    <item>
      <title>Charity money follows the news cycle. I built a Blink that splits it evenly instead</title>
      <dc:creator>Ali Hamza</dc:creator>
      <pubDate>Sun, 06 Sep 2026 22:04:16 +0000</pubDate>
      <link>https://dev.to/ali_hamza_4bd92ee9f67e78702/charity-money-follows-the-news-cycle-i-built-a-blink-that-splits-it-evenly-instead-83p</link>
      <guid>https://dev.to/ali_hamza_4bd92ee9f67e78702/charity-money-follows-the-news-cycle-i-built-a-blink-that-splits-it-evenly-instead-83p</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-09-03"&gt;Weekend Challenge: Generosity Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Generosity has a concentration problem. When a disaster happens, attention collapses onto whichever organisation is most visible that week, and money follows attention. Three charities can be doing equally good work in the same disaster zone, and one of them gets the donations because it got the news cycle. The donor almost never intended that outcome — splitting a gift by hand across three organisations means three websites, three checkout forms, three card entries, and nobody does that for $20.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OneClickGood&lt;/strong&gt; is a Solana Blink that makes the split the default instead of the effort. You click one amount, sign one transaction, and it lands in three vetted disaster-relief nonprofits at once — American Red Cross, Direct Relief and GlobalGiving — split evenly to the lamport.&lt;/p&gt;

&lt;p&gt;The important part is that the split is not a promise made in a paragraph like this one. It is three &lt;code&gt;SystemProgram.transfer&lt;/code&gt; instructions inside a single atomic transaction. Either all three nonprofits are funded or none of them are; the chain will not let me favour one and quietly under-fund the others. Every donation also carries an SPL Memo naming the amount and the basket, so the intent sits on the ledger next to the money.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In one line:&lt;/strong&gt; most donation projects use a blockchain to &lt;em&gt;record&lt;/em&gt; what happened. This one uses it to &lt;em&gt;enforce&lt;/em&gt; what happens, and then lets you audit it without trusting me.&lt;/p&gt;

&lt;p&gt;Four things make it different from a donate button with a wallet attached:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The even split is enforced by the runtime, not by my good intentions.&lt;/strong&gt; One transaction, three recipients, all-or-nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a Solana-native primitive, not an app.&lt;/strong&gt; A spec-conforming Action is a portable URL any Blink client can render and sign — not a page I own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It reads its own result back off the chain.&lt;/strong&gt; After you sign, the Blink chains to a completion screen built from the confirmed transaction's actual balance changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;There is no database.&lt;/strong&gt; Every number on the site is derived from devnet at request time, and a CLI reproduces all of them from an RPC node with zero access to my infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live: &lt;a href="https://oneclickgood.vercel.app" rel="noopener noreferrer"&gt;oneclickgood.vercel.app&lt;/a&gt;&lt;/strong&gt; — connect any Solana wallet on &lt;strong&gt;devnet&lt;/strong&gt; and try it.&lt;/p&gt;

&lt;p&gt;Here is the whole thing end to end: pick an amount, approve once in Phantom, and the Blink chains to a completion screen that reports what the &lt;em&gt;chain&lt;/em&gt; recorded — then the public ledger shows the donation the moment it confirms.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2gbe3bl072b26ls7gwvn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2gbe3bl072b26ls7gwvn.gif" alt="Donating 0.5 SOL through the OneClickGood Blink: one Phantom approval, then a completion screen showing the three-way split, then the on-chain ledger" width="560" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the completion screen. It does not echo back what my server intended to send — it fetches the confirmed transaction from devnet and reports the balance changes the chain actually recorded:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;0.5 SOL delivered to 3 nonprofits&lt;/strong&gt;&lt;br&gt;
Confirmed on devnet and read back from the chain — American Red Cross: 0.166666667 SOL · Direct Relief: 0.166666667 SOL · GlobalGiving: 0.166666666 SOL&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A real transaction from that flow, signed by a browser wallet: &lt;a href="https://explorer.solana.com/tx/28rjB9bHHaqrXfXNmmRLJJ3c6XdUm5Xd2EMKumpwRd9sLHrgCm6n4XBokb3NyPK7YgNnFAkVQyddgT1eeWvZcBro?cluster=devnet" rel="noopener noreferrer"&gt;&lt;code&gt;28rjB9bH…ZcBro&lt;/code&gt;&lt;/a&gt;. One signer, three recipients, one memo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't trust my site — audit it
&lt;/h3&gt;

&lt;p&gt;There is no database in this project. The &lt;a href="https://oneclickgood.vercel.app/ledger" rel="noopener noreferrer"&gt;public ledger&lt;/a&gt; is rendered by reading devnet at request time, which means my page can be wrong in only one direction: it can fail to load. It cannot quietly report a number the chain disagrees with.&lt;/p&gt;

&lt;p&gt;And you do not have to take the page's word either. &lt;code&gt;npm run verify&lt;/code&gt; talks to a Solana RPC node and nothing else — not my website, not my server. Given only the three wallet addresses, it walks every donation, decodes the memo, recomputes each split from the transaction's own &lt;code&gt;preBalances&lt;/code&gt;/&lt;code&gt;postBalances&lt;/code&gt;, and &lt;strong&gt;exits non-zero if any donation was not an even three-way split&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz516l20eds7lwftntxnh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz516l20eds7lwftntxnh.gif" alt="npm run verify walking every donation from an RPC node and confirming all splits were even" width="560" height="250"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0.5 SOL  even ✓  2026-09-06 19:45:58 UTC
  from BQoHF3RLx5saWhfK8kZWGkhJU4SXinym3D81kW27KbjW
    American Red Cross     0.166666667 SOL
    Direct Relief          0.166666667 SOL
    GlobalGiving           0.166666666 SOL
  memo: "OneClickGood: 0.5 SOL split across American Red Cross / Direct Relief / GlobalGiving"

Totals received:
  American Red Cross     0.733333336 SOL  (33.33%)
  Direct Relief          0.733333333 SOL  (33.33%)
  GlobalGiving           0.733333331 SOL  (33.33%)
  TOTAL                  2.2 SOL

Every one of the 5 donation(s) split evenly across all 3 nonprofits.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If that output and the website ever disagree, the chain is right and I am wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/unicorn-9-spec" rel="noopener noreferrer"&gt;
        unicorn-9-spec
      &lt;/a&gt; / &lt;a href="https://github.com/unicorn-9-spec/oneclickgood" rel="noopener noreferrer"&gt;
        oneclickgood
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Solana Blink that splits one donation across a vetted basket of disaster-relief nonprofits — one signature, three wallets, verifiable on-chain.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;OneClickGood — a donation Blink that splits to a vetted basket&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://oneclickgood.vercel.app" rel="nofollow noopener noreferrer"&gt;https://oneclickgood.vercel.app&lt;/a&gt; · &lt;strong&gt;On-chain ledger:&lt;/strong&gt; &lt;a href="https://oneclickgood.vercel.app/ledger" rel="nofollow noopener noreferrer"&gt;https://oneclickgood.vercel.app/ledger&lt;/a&gt;
&lt;em&gt;Submission for the &lt;a href="https://dev.to/challenges/weekend-2026-09-03" rel="nofollow"&gt;DEV Weekend Challenge: Generosity Edition&lt;/a&gt; — Best Use of Solana.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A Solana Blink that splits one donation evenly across three vetted disaster-relief nonprofits —
&lt;strong&gt;one signature, three recipients, one atomic transaction&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Charitable giving concentrates on whoever is most visible during a disaster. Splitting a gift by hand
across three organisations means three websites and three checkout forms, so nobody does it. This makes
the split the default instead of the effort — and because it is three &lt;code&gt;SystemProgram.transfer&lt;/code&gt;
instructions in a single transaction, the even split is enforced by the runtime rather than promised
by the operator. Either all three are funded or none are.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/unicorn-9-spec/oneclickgood/media/donate-flow.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2Funicorn-9-spec%2Foneclickgood%2FHEAD%2Fmedia%2Fdonate-flow.gif" alt="Donating through the OneClickGood Blink"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What's here&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /api/actions/donate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blink metadata — icon, title, preset and custom amounts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /api/actions/donate?amount=&amp;lt;SOL&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds the unsigned split transaction + SPL Memo, and&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/unicorn-9-spec/oneclickgood" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A Blink, not an app with a donate button.&lt;/strong&gt; This is the decision the whole project rests on. OneClickGood implements the &lt;a href="https://solana.com/docs/advanced/actions" rel="noopener noreferrer"&gt;Solana Actions&lt;/a&gt; spec: a &lt;code&gt;GET&lt;/code&gt; that returns the card metadata and available amounts, and a &lt;code&gt;POST&lt;/code&gt; that builds an unsigned transaction for whoever clicked. That makes the donation a &lt;em&gt;portable object&lt;/em&gt; rather than a page — the same URL renders and signs inside any Blink client, and &lt;code&gt;actions.json&lt;/code&gt; maps the domain so the whole site is discoverable as an Action provider. Nothing about it is specific to my frontend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The split, and the one bug that mattered.&lt;/strong&gt; Splitting evenly is integer division on lamports, and integer division leaves a remainder. Dropping it would silently shave lamports off the donation, so the remainder is distributed one lamport at a time across the first N charities — which is why the audit output reads &lt;code&gt;0.166666667 / 0.166666667 / 0.166666666&lt;/code&gt; rather than three identical numbers. That asymmetry is the remainder being spent rather than lost.&lt;/p&gt;

&lt;p&gt;The subtler bug: I initially allowed a minimum donation of 0.001 SOL. Split three ways that is ~0.00033 SOL each, which is &lt;strong&gt;below Solana's rent-exempt minimum&lt;/strong&gt; for a fresh account — so the transfer would have been built happily by my server and then rejected on-chain. The fix queries &lt;code&gt;getMinimumBalanceForRentExemption(0)&lt;/code&gt; at request time and refuses the donation with a real explanation, rather than handing the user a transaction that cannot land.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action chaining.&lt;/strong&gt; The &lt;code&gt;POST&lt;/code&gt; response returns &lt;code&gt;links.next&lt;/code&gt;, so after signing, the Blink client posts the signature to a completion endpoint. That endpoint fetches the transaction and diffs &lt;code&gt;preBalances&lt;/code&gt; against &lt;code&gt;postBalances&lt;/code&gt; for each charity account. This is why the success screen can state amounts as fact — they are read back off the chain, not remembered from a variable. Chaining is a spec feature that exists only &lt;em&gt;because&lt;/em&gt; this is an Action, and it turns the Blink from a fire-and-forget button into something that closes its own loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Charity data is real even though the wallets aren't.&lt;/strong&gt; Names, descriptions, logos and websites are pulled from the &lt;a href="https://www.every.org/charity-api" rel="noopener noreferrer"&gt;Every.org&lt;/a&gt; nonprofit API by &lt;strong&gt;EIN&lt;/strong&gt; — &lt;code&gt;530196605&lt;/code&gt; for the American Red Cross, &lt;code&gt;951831116&lt;/code&gt; for Direct Relief, &lt;code&gt;300108263&lt;/code&gt; for GlobalGiving — so the basket references verifiable 501(c)(3) records rather than three strings I typed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Being straight about the limits.&lt;/strong&gt; These are devnet wallets I generated, not addresses controlled by the Red Cross, and devnet SOL is not money. This is a proof of the &lt;em&gt;mechanism&lt;/em&gt; — that a one-signature multi-recipient donation is a real, auditable primitive — not a live fundraising channel. I would rather write that down than let a demo imply otherwise. Making it real is a wallet-custody and trust problem, not a code problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The thing that broke mid-build.&lt;/strong&gt; My original plan was to demo through &lt;code&gt;dial.to&lt;/code&gt;, Dialect's Blink interstitial, exactly as their docs still recommend. Partway through the weekend the domain stopped resolving — &lt;code&gt;NXDOMAIN&lt;/code&gt;, and not a local DNS hiccup; &lt;code&gt;dialect.to&lt;/code&gt; itself stayed up, so it looked like a lapsed registration rather than a migration. Rather than ship a demo that depends on somebody else's uptime, I embedded Dialect's own &lt;code&gt;@dialectlabs/blinks&lt;/code&gt; React client directly into the site, so the card renders and signs on my origin. The Blink is still spec-compliant and still portable; it just no longer needs a third party to be reachable to be seen. Two React bugs fell out of that move — a hydration mismatch from &lt;code&gt;WalletMultiButton&lt;/code&gt;, and passing the client a relative URL where it wanted an absolute one — and both are in the commit history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stack.&lt;/strong&gt; Next.js App Router on Vercel, &lt;code&gt;@solana/actions&lt;/code&gt; for the spec types and &lt;code&gt;createPostResponse&lt;/code&gt;, &lt;code&gt;@solana/web3.js&lt;/code&gt; for transaction building and every chain read, &lt;code&gt;@dialectlabs/blinks&lt;/code&gt; + wallet-adapter for the on-site renderer. No database anywhere in the project — devnet is the only state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Solana.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most donation projects use a chain as a payment rail: the app decides what happened, then writes a receipt. Here the chain is the &lt;em&gt;enforcement mechanism and the source of truth&lt;/em&gt;, in four concrete ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Atomicity is the product.&lt;/strong&gt; Even distribution across three nonprofits isn't a policy I promise to follow — it's three transfer instructions in one transaction. Partial favouritism is not something the runtime will execute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a Solana-native primitive, not an app.&lt;/strong&gt; A spec-conforming Action/Blink is portable and embeddable: the donation is a URL other surfaces can render and sign, which is exactly the composability Actions were introduced for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action chaining closes the loop.&lt;/strong&gt; &lt;code&gt;links.next&lt;/code&gt; lets the Blink read its own confirmed transaction back off devnet and report the real amounts. That capability exists only because it's an Action.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every number is chain-derived and independently auditable.&lt;/strong&gt; No database exists. The ledger page reads devnet live, and &lt;code&gt;npm run verify&lt;/code&gt; reproduces every figure from an RPC node with no access to my infrastructure — and fails loudly if a split was ever uneven.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remove Solana and this isn't a worse version of the project. It's just a promise in a blog post, which is the exact thing it was built to avoid.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>solana</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
