<?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>Forem: Ola Adesoye</title>
    <description>The latest articles on Forem by Ola Adesoye (@ola-zoll).</description>
    <link>https://forem.com/ola-zoll</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3518658%2F465a1b54-168b-4f1c-b19e-06252755a6f3.jpeg</url>
      <title>Forem: Ola Adesoye</title>
      <link>https://forem.com/ola-zoll</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ola-zoll"/>
    <language>en</language>
    <item>
      <title>A Rent Recovery Tool for Solana Accounts</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Sun, 25 Jan 2026 22:59:26 +0000</pubDate>
      <link>https://forem.com/ola-zoll/a-rent-recovery-tool-for-solana-accounts-22hf</link>
      <guid>https://forem.com/ola-zoll/a-rent-recovery-tool-for-solana-accounts-22hf</guid>
      <description>&lt;h2&gt;
  
  
  A Quick Solana Primer
&lt;/h2&gt;

&lt;p&gt;If you're not deep in the Solana ecosystem, here's a 30-second crash course.&lt;/p&gt;

&lt;p&gt;Solana is a blockchain — like Ethereum, but faster and cheaper. To do anything on Solana (send tokens, interact with apps, mint NFTs), you need to pay transaction fees. These fees are paid in &lt;strong&gt;SOL&lt;/strong&gt;, Solana's native currency.&lt;/p&gt;

&lt;p&gt;Here's where it gets interesting: every piece of data stored on Solana lives in something called an "account." Your wallet? An account. Your token balances? Each one is a separate account. And every account needs to hold a minimum amount of SOL to exist — this is called &lt;strong&gt;rent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like a security deposit. The blockchain holds onto this SOL to prevent spam (otherwise people could create infinite accounts and bloat the network). The good news? When you close an account, you get that deposit back.&lt;/p&gt;

&lt;p&gt;For a standard token account, this deposit is about &lt;strong&gt;0.002 SOL&lt;/strong&gt; (~$0.30-0.50 USD). Not much for one account. But what if you have thousands?&lt;/p&gt;




&lt;h2&gt;
  
  
  Kora for Gasless Transactions on Solana
&lt;/h2&gt;

&lt;p&gt;Now let's talk about &lt;strong&gt;Kora&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One of the friction points in crypto is that you always need the native token to do anything. Want to send USDC on Solana? You need SOL for the fee. Just got some tokens airdropped but have zero SOL? Too bad, you can't move them.&lt;/p&gt;

&lt;p&gt;Kora solves this. It's infrastructure enables &lt;strong&gt;gasless transactions&lt;/strong&gt;. Users can pay transaction fees in tokens they already have (like USDC or BONK) instead of SOL.&lt;/p&gt;

&lt;p&gt;Here's how it works at a high level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A user wants to make a transaction but doesn't have SOL for fees&lt;/li&gt;
&lt;li&gt;They submit their transaction to a Kora node&lt;/li&gt;
&lt;li&gt;The Kora node &lt;strong&gt;sponsors&lt;/strong&gt; the SOL fee on their behalf&lt;/li&gt;
&lt;li&gt;In return, the user pays the node in a token (like USDC)&lt;/li&gt;
&lt;li&gt;The transaction goes through, everyone's happy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kora nodes are essentially "paymasters" who front the SOL and get paid back in tokens.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Phantom Token Accounts
&lt;/h2&gt;

&lt;p&gt;Here's where my project comes in.&lt;/p&gt;

&lt;p&gt;Every time a Kora node receives a token payment from a user, Solana creates a &lt;strong&gt;token account&lt;/strong&gt; to hold those tokens. This happens automatically. The node accumulates USDC from one user, BONK from another, maybe some random memecoin from a third.&lt;/p&gt;

&lt;p&gt;Eventually, these tokens get swept to a treasury or converted to SOL. The token accounts become empty.&lt;/p&gt;

&lt;p&gt;But here's the catch: &lt;strong&gt;empty doesn't mean gone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Those accounts still exist on the blockchain. And they're still holding that rent deposit hostage — 0.002 SOL each, locked up and unusable.&lt;/p&gt;

&lt;p&gt;A busy Kora node might process thousands of transactions across hundreds of different tokens. That's potentially hundreds of empty token accounts. At 0.002 SOL each, we're talking about real money just... sitting there. Doing nothing.&lt;/p&gt;

&lt;p&gt;I built a tool to get it back.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tool
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Kora Rent-Reclaim&lt;/strong&gt; is a command-line tool that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Discovers&lt;/strong&gt; all token accounts owned by your wallet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filters&lt;/strong&gt; to find the ones that are empty and safe to close&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Closes&lt;/strong&gt; them and sends the rent back to an address you specify&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reports&lt;/strong&gt; exactly what happened for your records&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The concept is simple, but the execution required handling a lot of edge cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  The "Oh, That's How Solana Works" Moments
&lt;/h2&gt;

&lt;p&gt;There's always something to learn. Building this taught me a lot about Solana's internals. Here are the interesting bits:&lt;/p&gt;

&lt;h3&gt;
  
  
  Rent-Exempt Minimum
&lt;/h3&gt;

&lt;p&gt;Solana used to actually charge ongoing rent — your account balance would slowly decrease over time. They've since moved to a simpler model: hold a minimum balance, and your account lives forever. For token accounts, that magic number is exactly &lt;strong&gt;0.00203928 SOL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you close an account, this entire amount returns to you. It's not a fee — it's more like a refundable deposit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token Accounts Have Rules
&lt;/h3&gt;

&lt;p&gt;You can't just delete any account. Token accounts (technically called Associated Token Accounts or ATAs) follow specific rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only the &lt;strong&gt;owner&lt;/strong&gt; can close them&lt;/li&gt;
&lt;li&gt;They must have &lt;strong&gt;zero token balance&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;They must be in an &lt;strong&gt;initialized&lt;/strong&gt; state (not frozen)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try to close an account with tokens still in it? The blockchain rejects the transaction. This is actually great since it means the tool can't accidentally destroy your assets.&lt;/p&gt;

&lt;h3&gt;
  
  
  The getProgramAccounts Challenge
&lt;/h3&gt;

&lt;p&gt;To find all token accounts for a wallet, you query the Token Program with &lt;code&gt;getProgramAccounts&lt;/code&gt;. This returns every matching account in one response.&lt;/p&gt;

&lt;p&gt;For a wallet with 10,000 accounts? That's a heavy call. There's no pagination, no streaming. You get everything at once or nothing. This informed some of my design decisions around memory management.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building It: The Interesting Parts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Pipeline
&lt;/h3&gt;

&lt;p&gt;The tool follows a straightforward pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Discover → Filter → Reclaim → Report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Discover&lt;/strong&gt;: Query the RPC endpoint to get all token accounts owned by the signer wallet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter&lt;/strong&gt;: Categorize accounts into closeable (zero balance, initialized, not whitelisted) versus non-closeable (has balance, frozen, protected).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reclaim&lt;/strong&gt;: Bundle close instructions into transactions, send them, track successes and failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Report&lt;/strong&gt;: Display results to the user by writing to a CSV audit log.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling the Real World
&lt;/h3&gt;

&lt;p&gt;Production infrastructure is messy. Things fail. I built the tool to handle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RPC Rate Limiting&lt;/strong&gt;: Endpoints return 429 errors when you hit them too hard. The tool uses exponential backoff — wait 100ms, then 200ms, then 400ms between retries.&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;// Retry with exponential backoff&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;isRetryable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User Interruption&lt;/strong&gt;: Someone hits Ctrl+C mid-operation. The tool catches SIGINT, finishes the current transaction batch (no partial state), logs what completed, and exits cleanly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Low Balance Warning&lt;/strong&gt;: If your wallet doesn't have enough SOL to cover transaction fees, the tool warns you upfront rather than failing mysteriously later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Messages That Actually Help
&lt;/h3&gt;

&lt;p&gt;Every error follows a &lt;strong&gt;What / Why / Do&lt;/strong&gt; pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] Cannot read keypair file: ./missing.json
        File not found or not readable.
        → Check the path and ensure the file exists.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No cryptic stack traces and no generic error message like "something went wrong." Tell users what broke, why it matters, and exactly what to do about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dry-Run by Default
&lt;/h3&gt;

&lt;p&gt;The first time you run this tool, nothing happens. You see what &lt;em&gt;would&lt;/em&gt; happen. You have to explicitly pass &lt;code&gt;--execute&lt;/code&gt; to actually close accounts.&lt;/p&gt;

&lt;p&gt;Why? Because closing accounts is &lt;strong&gt;irreversible&lt;/strong&gt;. Once closed, the account is gone. Better to make the dangerous action opt-in rather than opt-out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Batching: 10 Accounts Per Transaction
&lt;/h3&gt;

&lt;p&gt;Solana transactions have compute limits. If you pack too many instructions into one transaction, it fails. Through testing, I landed on 10 close instructions per transaction — safe across different RPC providers and network conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Efficiency for Large Wallets
&lt;/h3&gt;

&lt;p&gt;For wallets with 10,000+ accounts, keeping everything in memory gets problematic. The tool writes history log entries in batches rather than accumulating them all until the end. This is a small change but it has significant impact on memory usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verbose Mode for Debugging
&lt;/h3&gt;

&lt;p&gt;When things go wrong, you need visibility. The &lt;code&gt;--verbose&lt;/code&gt; flag shows RPC call timing, slot numbers, and batch progress — all going to stderr so it doesn't interfere with JSON output if you're piping results somewhere.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;1. Solana's rent model is clever.&lt;/strong&gt; It solves blockchain state bloat by making storage have a real cost, but you get your money back when you're done. It's a deposit, not a tax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. CLI user experience matters more than you'd think.&lt;/strong&gt; Nobody reads documentation. The tool needs to guide users with first-run tips, clear defaults, and helpful error messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Defensive coding isn't paranoia — it's necessity.&lt;/strong&gt; RPCs fail. Users interrupt operations. Account states change between when you check and when you act. Handle all of it, or your tool becomes unreliable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. TypeScript for CLI tools is underrated.&lt;/strong&gt; Type safety caught countless bugs during development. The confidence to refactor without fear is worth the initial setup overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. The boring parts matter most.&lt;/strong&gt; Retry logic, graceful shutdown, audit logging — none of this is exciting to build, but it's what separates a script from a tool you can actually rely on.&lt;/p&gt;




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

&lt;p&gt;If you have a Solana wallet with old token accounts (and most active wallets do):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone and build&lt;/span&gt;
git clone https://github.com/Zolldyk/Auto-rent-reclaim-bot.git
&lt;span class="nb"&gt;cd &lt;/span&gt;Auto-rent-reclaim-bot
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build

&lt;span class="c"&gt;# Preview what you could reclaim (safe - no changes made)&lt;/span&gt;
npx kora-reclaim &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--signer-keypair&lt;/span&gt; ~/your-wallet.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--treasury&lt;/span&gt; YOUR_SOL_ADDRESS &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rpc-url&lt;/span&gt; https://api.mainnet-beta.solana.com

&lt;span class="c"&gt;# Actually reclaim (when you're ready)&lt;/span&gt;
npx kora-reclaim &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--signer-keypair&lt;/span&gt; ~/your-wallet.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--treasury&lt;/span&gt; YOUR_SOL_ADDRESS &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rpc-url&lt;/span&gt; https://api.mainnet-beta.solana.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--execute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might be surprised how much SOL is sitting in empty accounts and waiting to be reclaimed.&lt;/p&gt;




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

&lt;p&gt;This project started as a specific solution for Kora node operators but works for anyone with unused token accounts on Solana. The core insight is simple: those empty accounts aren't free — they're holding your SOL.&lt;/p&gt;

&lt;p&gt;The implementation taught me a lot about Solana's architecture, building reliable CLI tools, and the importance of handling edge cases gracefully.&lt;/p&gt;

&lt;p&gt;Sometimes the best tools are the ones that solve one specific problem really well.&lt;/p&gt;




</description>
      <category>web3</category>
      <category>blockchain</category>
      <category>solana</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built a Hacking Playground for Solana So You Don't Have to Lose Real Money Learning</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Sun, 25 Jan 2026 21:27:23 +0000</pubDate>
      <link>https://forem.com/ola-zoll/i-built-a-hacking-playground-for-solana-so-you-dont-have-to-lose-real-money-learning-21df</link>
      <guid>https://forem.com/ola-zoll/i-built-a-hacking-playground-for-solana-so-you-dont-have-to-lose-real-money-learning-21df</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I created an open-source educational repository with intentionally vulnerable Solana smart contracts. You can legally hack them, watch exploits work in real-time, then study the secure versions to understand exactly what went wrong. It's like a flight simulator for blockchain security.&lt;/p&gt;




&lt;h2&gt;
  
  
  The $300 Million Wake-Up Call
&lt;/h2&gt;

&lt;p&gt;In 2022, the Wormhole bridge lost $320 million in a single exploit. The Cashio stablecoin collapsed overnight. Slope wallet leaked private keys. These weren't random accidents—they were predictable security failures that could have been prevented.&lt;/p&gt;

&lt;p&gt;What's crazy is not only the money you realize was lost in those exploits, but also that the same vulnerability patterns keep appearing over and over. Missing signature checks, unchecked math overflow, and authority validation failures. &lt;/p&gt;

&lt;p&gt;The same mistakes, different projects, millions gone.&lt;/p&gt;

&lt;p&gt;Anyway, here's a safe place to &lt;em&gt;experience&lt;/em&gt; these vulnerabilities firsthand. &lt;/p&gt;




&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/Zolldyk/anchor-pinochio-security-template" rel="noopener noreferrer"&gt;Anchor/Pinocchio Security Template&lt;/a&gt;&lt;/strong&gt; is a collection of six vulnerability patterns, each containing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A deliberately broken smart contract&lt;/strong&gt; — with intentional security flaws&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A secure version&lt;/strong&gt; — showing exactly how to fix it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working exploit code&lt;/strong&gt; — that actually drains the vulnerable contract&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tests proving the secure version blocks attacks&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything runs on a local Solana validator. No real money. No devnet tokens. Just you, the code, and a safe environment to break things.&lt;/p&gt;

&lt;h3&gt;
  
  
  The six patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What Goes Wrong&lt;/th&gt;
&lt;th&gt;Real-World Parallel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Missing Validation&lt;/td&gt;
&lt;td&gt;No check if caller is authorized&lt;/td&gt;
&lt;td&gt;Leaving your front door unlocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authority Failures&lt;/td&gt;
&lt;td&gt;Admin functions anyone can call&lt;/td&gt;
&lt;td&gt;Giving everyone the master key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unsafe Arithmetic&lt;/td&gt;
&lt;td&gt;Numbers overflow and wrap around&lt;/td&gt;
&lt;td&gt;Your bank balance going from $1 to $18 quintillion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPI Re-entrancy&lt;/td&gt;
&lt;td&gt;Contract calls back into itself mid-transaction&lt;/td&gt;
&lt;td&gt;Someone withdrawing money while the ATM is still counting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PDA Derivation&lt;/td&gt;
&lt;td&gt;Wrong address generation lets attackers substitute accounts&lt;/td&gt;
&lt;td&gt;Someone redirecting your mail to their house&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Token Validation&lt;/td&gt;
&lt;td&gt;Accepting fake tokens as real ones&lt;/td&gt;
&lt;td&gt;A vending machine that accepts photocopied bills&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  A side-by-side comparison that changes everything
&lt;/h2&gt;

&lt;p&gt;Most security tutorials show you the "right way" and leave it at that. It might also be great to learn by seeing &lt;em&gt;what not to do&lt;/em&gt; first.&lt;/p&gt;

&lt;p&gt;Here's the thing: when you only see correct code, you don't develop intuition for what's dangerous. You follow patterns without understanding why they exist.&lt;/p&gt;

&lt;p&gt;But when you see this vulnerable code:&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="c1"&gt;// VULNERABILITY: Anyone can call this!&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;transfer&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;Transfer&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;amount&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;// No check if caller is the owner&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.vault.balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then run an exploit that actually steals funds from it... suddenly the secure version makes visceral sense:&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="c1"&gt;// SECURITY: Only the owner can transfer&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;transfer&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;Transfer&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;amount&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="nd"&gt;require!&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.authority&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.vault.owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;ErrorCode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Unauthorized&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.vault.balance&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;require!&lt;/code&gt; check isn't just "best practice" anymore. It's the line between your program working and someone draining everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  The dual-framework approach
&lt;/h2&gt;

&lt;p&gt;One thing that sets this project apart: every vulnerability is implemented in both &lt;strong&gt;Anchor&lt;/strong&gt; (the popular high-level framework) and &lt;strong&gt;Pinocchio&lt;/strong&gt; (a minimal, lower-level library).&lt;/p&gt;

&lt;p&gt;Why bother?&lt;/p&gt;

&lt;p&gt;Because security principles are framework-agnostic. Whether you're using Anchor's convenient macros or writing raw account parsing with Pinocchio, the same vulnerabilities exist. &lt;/p&gt;

&lt;p&gt;Seeing them in both contexts drives home that security isn't about memorizing framework-specific tricks - it's about understanding the underlying model.&lt;/p&gt;

&lt;p&gt;Plus, Pinocchio implementations show you what Anchor is doing "under the hood." When you see the 50 lines of manual validation that Anchor's &lt;code&gt;#[account(...)]&lt;/code&gt; macro generates for you, you appreciate both the convenience AND understand what could go wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to use this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Clone and set up:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Zolldyk/anchor-pinochio-security-template.git
&lt;span class="nb"&gt;cd &lt;/span&gt;anchor-pinochio-security-template
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Start with Pattern 01 (Missing Validation):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run &lt;span class="nb"&gt;test&lt;/span&gt;:01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vulnerable program
  ✗ allows unauthorized balance update (demonstrates vulnerability)
Secure Program
  ✓ blocks unauthorized balance update (demonstrates fix)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;✗&lt;/code&gt; means the exploit succeeded. The &lt;code&gt;✓&lt;/code&gt; means the attack was blocked. Both are expected!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Read the code, modify the exploit, break things.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/Zolldyk/anchor-pinochio-security-template/blob/master/LEARNING_PATH.md" rel="noopener noreferrer"&gt;LEARNING_PATH.md&lt;/a&gt; provides a structured progression from beginner to advanced patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned building this
&lt;/h2&gt;

&lt;p&gt;A few things surprised me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anchor does a lot of heavy lifting.&lt;/strong&gt; Starting from the &lt;code&gt;Signer&amp;lt;'info&amp;gt;&lt;/code&gt; type, to the &lt;code&gt;has_one&lt;/code&gt; constraint, and the &lt;code&gt;#[account(...)]&lt;/code&gt; macro. They aren't just conveniences; they're guardrails that prevent entire categories of vulnerabilities. &lt;/p&gt;

&lt;p&gt;The Pinocchio implementations made me appreciate how much validation Anchor handles automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arithmetic bugs are sneakier than I expected.&lt;/strong&gt; Integer overflow doesn't throw an error in Rust by default (in release mode). Your balance just quietly wraps from 0 to 18,446,744,073,709,551,615. The checked math functions (&lt;code&gt;checked_add&lt;/code&gt;, &lt;code&gt;checked_sub&lt;/code&gt;) exist for a reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-entrancy in Solana is different from Ethereum.&lt;/strong&gt; Solana's account model makes traditional re-entrancy harder, but CPI (Cross-Program Invocation) introduces its own version. The "checks-effects-interactions" pattern matters here too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security is about layers.&lt;/strong&gt; No single check makes a program secure. It's the combination of signer validation, owner checks, PDA derivation, bounds checking, and careful state management that creates defense in depth.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solana developers&lt;/strong&gt; who want to level up their security knowledge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auditors&lt;/strong&gt; looking for a reference of common vulnerability patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web3 security enthusiasts&lt;/strong&gt; interested in hands-on learning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anyone curious&lt;/strong&gt; about how smart contract exploits actually work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to be a Solana expert. If you know basic Rust and understand that blockchains have "smart contracts," you can follow along.&lt;/p&gt;




&lt;p&gt;Happy (secure) coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>web3</category>
      <category>blockchain</category>
      <category>solana</category>
    </item>
    <item>
      <title>Building a Biometric Crypto Wallet (That Actually Works!)</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Wed, 14 Jan 2026 00:05:25 +0000</pubDate>
      <link>https://forem.com/ola-zoll/building-a-biometric-crypto-wallet-that-actually-works-2ble</link>
      <guid>https://forem.com/ola-zoll/building-a-biometric-crypto-wallet-that-actually-works-2ble</guid>
      <description>&lt;p&gt;&lt;em&gt;Or: How I Learned to Stop Worrying and Love WebAuthn Passkeys&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The "Why Did I Do This Again?" Moment
&lt;/h2&gt;

&lt;p&gt;So, I built a mobile crypto wallet. But not just any wallet – one where you create your wallet using your face (or fingerprint), and send money without paying gas fees. Sounds cool, right? Well, it kind of is, and I'm here to tell you it's also kind of not.&lt;/p&gt;

&lt;p&gt;Let me back up.&lt;/p&gt;

&lt;p&gt;A few weeks ago, I stumbled upon this thing called Lazorkit. They had this wild idea: what if instead of making people memorize 12-word seed phrases (which, let's be honest, most people just screenshot and pray their phones don't get stolen), we used biometric authentication? You know, the Face ID thing you use to unlock your phone 50 times a day anyway?&lt;/p&gt;

&lt;p&gt;Challenge accepted.&lt;/p&gt;

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

&lt;p&gt;The app is pretty straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You tap "Create Wallet."&lt;/li&gt;
&lt;li&gt;Your phone asks for Face ID or fingerprint&lt;/li&gt;
&lt;li&gt;Boom. You have a Solana wallet.&lt;/li&gt;
&lt;li&gt;You can send USDC tokens without having SOL for gas fees (gasless transactions via a paymaster)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No seed phrases. No "write this down on a piece of paper and hide it in your mattress." Just your face.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React Native (with Expo SDK 54)&lt;/li&gt;
&lt;li&gt;TypeScript (because I like knowing what's breaking before it breaks)&lt;/li&gt;
&lt;li&gt;Solana blockchain (Devnet for testing)&lt;/li&gt;
&lt;li&gt;Lazorkit SDK (the magic sauce)&lt;/li&gt;
&lt;li&gt;WebAuthn passkeys (the real magic sauce)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The "Oh Crap" Moments (aka What I Learned)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Polyfills Are Your Secret Enemies
&lt;/h3&gt;

&lt;p&gt;First lesson: React Native doesn't have all the JavaScript globals that web browsers have. Solana's SDK expects things like &lt;code&gt;Buffer&lt;/code&gt; and &lt;code&gt;crypto.getRandomValues()&lt;/code&gt; to just... exist. They don't.&lt;/p&gt;

&lt;p&gt;I spent 2 hours debugging a cryptic error about randomness before realizing I needed to import polyfills. And not just import them – import them in the EXACT RIGHT ORDER at the very top of my app.&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;// This order matters. Don't ask me why. I've been hurt before.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-url-polyfill/auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Buffer&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="s1"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-get-random-values&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move these around? Your app explodes. Fun times.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Simulators Are Liars
&lt;/h3&gt;

&lt;p&gt;You can't test biometric authentication properly on a simulator. I mean, you &lt;em&gt;can&lt;/em&gt;, but it's like testing a swimming pool by looking at a picture of water. The iOS simulator has a "fake Face ID" feature that sort of works, but deep linking to the Lazorkit Portal and back? Yeah, that's where things get weird.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hot tip:&lt;/strong&gt; Get a physical device. Test on real hardware. Trust me on this one.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. WebAuthn Is Actually Pretty Cool
&lt;/h3&gt;

&lt;p&gt;Here's the thing nobody told me: WebAuthn passkeys are just public-key cryptography with better UX. When you create a wallet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your phone generates a key pair&lt;/li&gt;
&lt;li&gt;The private key stays in your device's secure enclave (the same place your fingerprint data lives)&lt;/li&gt;
&lt;li&gt;The public key goes to Lazorkit's Portal service&lt;/li&gt;
&lt;li&gt;When you need to sign something, your phone uses Face ID to unlock the private key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's like having a hardware wallet, but your phone IS the hardware wallet. Mind. Blown.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Gasless Transactions Are Secretly Not Gasless
&lt;/h3&gt;

&lt;p&gt;Plot twist: "gasless" transactions still have gas fees. You just don't pay them. A paymaster does.&lt;/p&gt;

&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You create a transaction (send 1 USDC to your friend)&lt;/li&gt;
&lt;li&gt;The paymaster service looks at it and says "cool, I'll pay the SOL fee"&lt;/li&gt;
&lt;li&gt;The paymaster signs the transaction with their wallet&lt;/li&gt;
&lt;li&gt;You sign it with your biometric wallet&lt;/li&gt;
&lt;li&gt;Transaction goes through, you paid $0&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Catch:&lt;/strong&gt; The paymaster won't create token accounts for you. If your recipient doesn't have a USDC account already, the transaction fails. This burned me during testing until I realized I could just send USDC to myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Deep Linking Is Dark Magic
&lt;/h3&gt;

&lt;p&gt;To make the biometric authentication work, the app needs to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Safari/Chrome&lt;/li&gt;
&lt;li&gt;Load the Lazorkit Portal page&lt;/li&gt;
&lt;li&gt;Trigger Face ID&lt;/li&gt;
&lt;li&gt;Redirect back to your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This requires deep linking configuration in &lt;code&gt;app.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lazorkitstarter"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Getting this right took me 3 tries because I kept using weird characters. Keep it simple: lowercase, no spaces, no special characters. Future you will thank present you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Wait, This Actually Works?" Moment
&lt;/h2&gt;

&lt;p&gt;After wrestling with polyfills, deep links, and trying to understand why my transaction kept failing (spoiler: I had no test USDC), I finally got it working.&lt;/p&gt;

&lt;p&gt;I opened the app, tapped "Create Wallet," looked at my phone for Face ID, and BAM – I had a Solana wallet. &lt;/p&gt;

&lt;p&gt;No seed phrases. No friction.&lt;/p&gt;

&lt;p&gt;It felt like science fiction, but it's just good API design.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tutorials (Because I Like You)
&lt;/h2&gt;

&lt;p&gt;I wrote two full tutorials for this project so you can follow along:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tutorial 1: &lt;a href="https://dev.to/ola-zoll/wallet-creation-with-biometric-authentication-1j6l"&gt;Wallet Creation with Biometric Authentication&lt;/a&gt;&lt;/strong&gt; (~30 min)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up polyfills (the right way)&lt;/li&gt;
&lt;li&gt;Configuring LazorKitProvider&lt;/li&gt;
&lt;li&gt;Deep linking between your app and the Portal&lt;/li&gt;
&lt;li&gt;Handling biometric authentication callbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tutorial 2: &lt;a href="https://dev.to/ola-zoll/gasless-usdc-transactions-with-lazorkit-paymaster-356b"&gt;Gasless USDC Transactions&lt;/a&gt;&lt;/strong&gt; (~40 min)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building Solana transactions with web3.js&lt;/li&gt;
&lt;li&gt;Integrating paymaster for gasless fees&lt;/li&gt;
&lt;li&gt;Proper blockhash management (because they expire after 60 seconds and will ruin your day)&lt;/li&gt;
&lt;li&gt;Error handling and validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Is This For?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You should build this if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're learning React Native and want a real-world project&lt;/li&gt;
&lt;li&gt;You're curious about Solana mobile development&lt;/li&gt;
&lt;li&gt;You want to understand how WebAuthn passkeys work&lt;/li&gt;
&lt;li&gt;You like the idea of crypto wallets without seed phrases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You should NOT build this if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need production-ready code (this is Devnet only, folks)&lt;/li&gt;
&lt;li&gt;You hate TypeScript&lt;/li&gt;
&lt;li&gt;You don't have a physical iPhone or Android device for testing&lt;/li&gt;
&lt;li&gt;You're allergic to polyfills&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I built a mobile crypto wallet with Face ID authentication and gasless transactions. It uses WebAuthn passkeys (no seed phrases!), runs on Solana Devnet, and actually works. I learned way too much about polyfills, deep linking, and why simulators are not your friends.&lt;/p&gt;

&lt;p&gt;If you're learning mobile dev or blockchain stuff, give it a shot. If nothing else, you'll have a very strong opinion about import order.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built this? Got stuck? Found a bug? Hit me up – I'm always down to talk about why something broke.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; If you try this and your app crashes because of polyfill order, just know I told you so.😅&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>web3</category>
      <category>solana</category>
    </item>
    <item>
      <title>Gasless USDC Transactions with Lazorkit Paymaster</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Tue, 13 Jan 2026 23:50:24 +0000</pubDate>
      <link>https://forem.com/ola-zoll/gasless-usdc-transactions-with-lazorkit-paymaster-356b</link>
      <guid>https://forem.com/ola-zoll/gasless-usdc-transactions-with-lazorkit-paymaster-356b</guid>
      <description>&lt;p&gt;In traditional blockchain applications, users need native cryptocurrency (like SOL on Solana) to pay for transaction fees, even when they're just sending tokens like USDC. This creates a major onboarding friction: users must acquire SOL before they can use your app to send USDC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gasless transactions solve this problem.&lt;/strong&gt; With Lazorkit's paymaster integration, users can send USDC without holding any SOL. The paymaster service sponsors the transaction fees on behalf of the user by creating a seamless experience where users only need the tokens they want to send.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Paymaster?
&lt;/h3&gt;

&lt;p&gt;A paymaster is a third-party service that pays transaction fees on behalf of users. Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User builds and signs transaction&lt;/strong&gt; - The user creates a transfer transaction and signs it with their biometric authentication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paymaster receives signed transaction&lt;/strong&gt; - The Lazorkit SDK automatically sends the signed transaction to the paymaster service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paymaster adds fee payment&lt;/strong&gt; - The paymaster validates the transaction and adds a fee payer instruction (using their own SOL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paymaster submits to blockchain&lt;/strong&gt; - The sponsored transaction is submitted to the Solana network&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Trustless security:&lt;/strong&gt; Users only sign the transfer instruction itself, not the fee payment. The paymaster adds its own fee payment after receiving the user's signature. This means users always know exactly what they're signing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before starting this tutorial, you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Completed &lt;a href="https://dev.to/ola-zoll/wallet-creation-with-biometric-authentication-1j6l"&gt;Tutorial 1: Wallet Creation with Biometric Authentication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic understanding of React Native and Expo&lt;/li&gt;
&lt;li&gt;Familiarity with Solana blockchain concepts (accounts, transactions, tokens)&lt;/li&gt;
&lt;li&gt;Knowledge of TypeScript and async/await patterns&lt;/li&gt;
&lt;li&gt;Understanding of &lt;code&gt;@solana/web3.js&lt;/code&gt; library basics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What You'll Build
&lt;/h3&gt;

&lt;p&gt;In this tutorial, you'll implement a complete gasless USDC transfer flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Transfer Form&lt;/strong&gt; - Input validation for recipient address and amount&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction Preview&lt;/strong&gt; - Display transaction details before signing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction Building&lt;/strong&gt; - Construct SPL Token transfer instruction using @solana/web3.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Biometric Signing&lt;/strong&gt; - Trigger Face ID/Touch ID authentication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gasless Submission&lt;/strong&gt; - Automatic paymaster sponsorship via Lazorkit SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirmation Polling&lt;/strong&gt; - Monitor blockchain for transaction confirmation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Success Display&lt;/strong&gt; - Show transaction details with Solana Explorer link&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Time Required
&lt;/h3&gt;

&lt;p&gt;Approximately &lt;strong&gt;30-40 minutes&lt;/strong&gt; to complete all steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ola-zoll/wallet-creation-with-biometric-authentication-1j6l"&gt;Tutorial 1: Wallet Creation&lt;/a&gt; - Complete wallet setup first&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Understanding USDC SPL Token Transfers
&lt;/h2&gt;

&lt;p&gt;Before building transactions, it's important to understand how USDC transfers work on Solana.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the SPL Token Program?
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Solana Program Library (SPL)&lt;/strong&gt; Token Program is the standard for creating and managing fungible tokens on Solana. USDC on Solana is an SPL Token, just like thousands of other tokens on the network.&lt;/p&gt;

&lt;p&gt;Key concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mint Address&lt;/strong&gt;: Every SPL Token has a unique mint address that identifies the token type (like USDC)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token Accounts&lt;/strong&gt;: Each wallet needs a separate "token account" to hold each type of SPL Token&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Associated Token Accounts (ATAs)&lt;/strong&gt;: Deterministic addresses derived from wallet address + mint address&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  USDC Mint Address
&lt;/h3&gt;

&lt;p&gt;In this project, we use the Devnet USDC mint address:&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;// Source: services/constants.ts:127-129&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;USDC_MINT_DEVNET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What is a mint address?&lt;/strong&gt; Think of it as the "contract address" or unique identifier for USDC on Solana. All USDC tokens on Devnet share this same mint address.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Mainnet USDC has a different mint address. Always verify you're using the correct mint for your target network.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Associated Token Accounts (ATAs)
&lt;/h3&gt;

&lt;p&gt;To hold USDC, each wallet needs an &lt;strong&gt;Associated Token Account (ATA)&lt;/strong&gt;. The ATA address is deterministically derived from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The wallet's public key (owner)&lt;/li&gt;
&lt;li&gt;The token mint address (e.g., USDC)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Source: services/SolanaService.ts:115-119&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;senderTokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;usdcMint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// USDC mint address&lt;/span&gt;
  &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// Wallet owner's public key&lt;/span&gt;
  &lt;span class="kc"&gt;true&lt;/span&gt;                &lt;span class="c1"&gt;// allowOwnerOffCurve - CRITICAL for smart wallets!&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  CRITICAL: Smart Wallet Compatibility
&lt;/h4&gt;

&lt;p&gt;The third parameter &lt;code&gt;allowOwnerOffCurve: true&lt;/code&gt; is &lt;strong&gt;required&lt;/strong&gt; for Lazorkit smart wallets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why?&lt;/strong&gt; Lazorkit smart wallets use Program Derived Addresses (PDAs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What are PDAs?&lt;/strong&gt; Deterministic addresses generated using seeds (not random keypairs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The issue:&lt;/strong&gt; PDAs are intentionally "off the ed25519 curve" (not valid elliptic curve points)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fix:&lt;/strong&gt; Setting &lt;code&gt;allowOwnerOffCurve: true&lt;/code&gt; tells the SDK to allow these off-curve addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Without this parameter, you'll get &lt;code&gt;TokenOwnerOffCurveError&lt;/code&gt;&lt;/strong&gt; and transactions will fail.&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;// WRONG - Will fail for smart wallets&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT - Works with smart wallets (PDAs)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SPL Token Transfer Instruction
&lt;/h3&gt;

&lt;p&gt;Once you have both sender and recipient token accounts, you create a transfer instruction:&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;// Source: services/SolanaService.ts:140-147&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transferInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTransferInstruction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;senderTokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Source: sender's USDC token account&lt;/span&gt;
  &lt;span class="nx"&gt;recipientTokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// Destination: recipient's USDC token account&lt;/span&gt;
  &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// Owner: wallet signing the transfer&lt;/span&gt;
  &lt;span class="nx"&gt;amountLamports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// Amount: USDC in lamports (6 decimals)&lt;/span&gt;
  &lt;span class="p"&gt;[],&lt;/span&gt;                      &lt;span class="c1"&gt;// Multi-signers: empty for single signature&lt;/span&gt;
  &lt;span class="nx"&gt;TOKEN_PROGRAM_ID&lt;/span&gt;         &lt;span class="c1"&gt;// Program: SPL Token program&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Parameters explained:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: The sender's USDC token account (where USDC comes from)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination&lt;/strong&gt;: The recipient's USDC token account (where USDC goes)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Owner&lt;/strong&gt;: The sender's wallet public key (must sign the transaction)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amount&lt;/strong&gt;: The amount to transfer in &lt;strong&gt;lamports&lt;/strong&gt; (see below)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-signers&lt;/strong&gt;: Empty array for single-signature wallets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Program ID&lt;/strong&gt;: The SPL Token program that processes this instruction&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  USDC Lamports Conversion
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; USDC has 6 decimal places, just like USD cents but with 4 more digits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;1 USDC = 1,000,000 lamports&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10.50 USDC = 10,500,000 lamports&lt;/li&gt;
&lt;li&gt;0.01 USDC = 10,000 lamports&lt;/li&gt;
&lt;li&gt;100 USDC = 100,000,000 lamports
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Source: utils/ValidationUtils.ts:125-128&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatUSDCAmountToLamports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// USDC has 6 decimals, so multiply by 1,000,000&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;_000_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This conversion is critical - transactions use lamports, not decimal amounts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Building the Transfer Transaction
&lt;/h2&gt;

&lt;p&gt;Now that you understand SPL Token transfers, let's implement the transaction building logic using the &lt;code&gt;SolanaService&lt;/code&gt; class.&lt;/p&gt;

&lt;h3&gt;
  
  
  The buildUSDCTransfer Method
&lt;/h3&gt;

&lt;p&gt;The complete transaction building flow is encapsulated in &lt;code&gt;SolanaService.buildUSDCTransfer()&lt;/code&gt;:&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;// Source: services/SolanaService.ts:101-161&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;buildUSDCTransfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;senderPublicKey&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Transaction&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;// 1. Initialize RPC connection&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConnection&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;usdcMint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUSDCMintAddress&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Parse recipient address to PublicKey&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipientPublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recipientAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Derive sender's USDC token account (ATA)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;senderTokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;usdcMint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// allowOwnerOffCurve - CRITICAL for smart wallets!&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Derive recipient's USDC token account (ATA)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipientTokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;usdcMint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;recipientPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// allowOwnerOffCurve - recipient may also use smart wallet&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 5. CRITICAL VALIDATION: Check recipient account exists&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipientAccountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;recipientTokenAccount&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipientAccountInfo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient USDC token account not initialized. They must receive USDC at least once before you can send to them.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 6. Create SPL Token transfer instruction&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transferInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTransferInstruction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;senderTokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Source&lt;/span&gt;
    &lt;span class="nx"&gt;recipientTokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// Destination&lt;/span&gt;
    &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// Owner&lt;/span&gt;
    &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amountLamports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Amount in lamports&lt;/span&gt;
    &lt;span class="p"&gt;[],&lt;/span&gt;                      &lt;span class="c1"&gt;// No multi-signers&lt;/span&gt;
    &lt;span class="nx"&gt;TOKEN_PROGRAM_ID&lt;/span&gt;         &lt;span class="c1"&gt;// SPL Token program&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 7. Build transaction with instruction&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferInstruction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 8. Set fee payer (will be overridden by paymaster)&lt;/span&gt;
  &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;feePayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 9. Fetch recent blockhash&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;blockhash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLatestBlockhash&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentBlockhash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blockhash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;transaction&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;h3&gt;
  
  
  Step-by-Step Breakdown
&lt;/h3&gt;

&lt;p&gt;Let's understand each step in detail:&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1-2: Initialize Connection and Parse Address
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConnection&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;usdcMint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUSDCMintAddress&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;recipientPublicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recipientAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection&lt;/strong&gt;: Create RPC connection to Solana Devnet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USDC Mint&lt;/strong&gt;: Get the USDC token mint address constant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recipient PublicKey&lt;/strong&gt;: Convert recipient address string to PublicKey object&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 3-4: Derive Token Accounts
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;senderTokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;usdcMint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// allowOwnerOffCurve - CRITICAL!&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;allowOwnerOffCurve: true&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lazorkit smart wallets use Program Derived Addresses (PDAs)&lt;/li&gt;
&lt;li&gt;PDAs are "off the ed25519 curve" by design&lt;/li&gt;
&lt;li&gt;Without this parameter, &lt;code&gt;getAssociatedTokenAddress()&lt;/code&gt; throws &lt;code&gt;TokenOwnerOffCurveError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This applies to BOTH sender and recipient (either could use smart wallets)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 5: Validate Recipient Account Exists ⚠️
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipientAccountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAccountInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipientTokenAccount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipientAccountInfo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Recipient USDC token account not initialized...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why is this validation critical?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Paymaster won't initialize accounts&lt;/strong&gt; - Creating token accounts costs SOL, and paymasters won't do this (too expensive and abusable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction would fail&lt;/strong&gt; - Sending to a non-existent account fails on-chain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better user experience&lt;/strong&gt; - Fail fast with clear error before signing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;When does this happen?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recipient has never received USDC before&lt;/li&gt;
&lt;li&gt;Their USDC token account doesn't exist yet&lt;/li&gt;
&lt;li&gt;They need to receive USDC from another source first (faucet, exchange, friend)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Step 6: Create Transfer Instruction
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transferInstruction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTransferInstruction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;senderTokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Where USDC comes from&lt;/span&gt;
  &lt;span class="nx"&gt;recipientTokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// Where USDC goes&lt;/span&gt;
  &lt;span class="nx"&gt;senderPublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// Who signs the transfer&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amountLamports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// How much (in lamports)&lt;/span&gt;
  &lt;span class="p"&gt;[],&lt;/span&gt;                      &lt;span class="c1"&gt;// Multi-sig signers (none)&lt;/span&gt;
  &lt;span class="nx"&gt;TOKEN_PROGRAM_ID&lt;/span&gt;         &lt;span class="c1"&gt;// SPL Token program ID&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instruction tells the SPL Token program to move USDC from sender to recipient.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 7-9: Build Transaction
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transferInstruction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;feePayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;senderPublicKey&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;blockhash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLatestBlockhash&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recentBlockhash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blockhash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fee Payer&lt;/strong&gt;: Set to sender's wallet, but &lt;strong&gt;paymaster will override this&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Blockhash&lt;/strong&gt;: Acts as a timestamp to prevent transaction replay. Valid for ~60 seconds.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Always fetch blockhash immediately before signing. Don't pre-build transactions minutes in advance, or the blockhash will become stale and the transaction will be rejected.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  The TransactionRequest Interface
&lt;/h3&gt;

&lt;p&gt;The method accepts a &lt;code&gt;TransactionRequest&lt;/code&gt; object from the transfer form:&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;// Source: types/index.ts:45-77&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TransactionRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;recipientAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Base58-encoded Solana address&lt;/span&gt;
  &lt;span class="nl"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;              &lt;span class="c1"&gt;// USDC in decimal format (e.g., 10.50)&lt;/span&gt;
  &lt;span class="nl"&gt;amountLamports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// USDC in lamports (e.g., 10,500,000)&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Unix timestamp (milliseconds)&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 created by the transfer form and passed via navigation params.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Configuring the Paymaster
&lt;/h2&gt;

&lt;p&gt;The paymaster integration is configured once at the application root using &lt;code&gt;LazorKitProvider&lt;/code&gt;. Once configured, all transactions automatically benefit from gas sponsorship with no additional code required.&lt;/p&gt;

&lt;h3&gt;
  
  
  LazorKitProvider Configuration
&lt;/h3&gt;

&lt;p&gt;In your root layout file, wrap your application with &lt;code&gt;LazorKitProvider&lt;/code&gt;:&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;// Source: app/_layout.tsx:22-28&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LazorKitProvider&lt;/span&gt;
  &lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;portalUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://portal.lazor.sh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;configPaymaster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
    &lt;span class="na"&gt;paymasterUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://kora.devnet.lazorkit.com&lt;/span&gt;&lt;span class="dl"&gt;'&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LazorKitProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration Parameters Explained
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;rpcUrl&lt;/code&gt; - Solana RPC Endpoint
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Defines the Solana blockchain RPC endpoint for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Querying account data (balances, token accounts)&lt;/li&gt;
&lt;li&gt;Submitting transactions to the network&lt;/li&gt;
&lt;li&gt;Confirming transaction status&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Devnet: &lt;code&gt;https://api.devnet.solana.com&lt;/code&gt; (for development)&lt;/li&gt;
&lt;li&gt;Mainnet: &lt;code&gt;https://api.mainnet-beta.solana.com&lt;/code&gt; (for production)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The public RPC has rate limits (~40 requests per 10 seconds). For production apps, use a paid RPC provider like QuickNode, Alchemy, or Helius.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;portalUrl&lt;/code&gt; - Lazorkit Portal for Authentication
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;portalUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://portal.lazor.sh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Lazorkit Portal URL for WebAuthn passkey authentication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles biometric credential creation (Face ID/Touch ID)&lt;/li&gt;
&lt;li&gt;Manages passkey authentication flow&lt;/li&gt;
&lt;li&gt;Returns signed transactions to your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; When signing transactions, your app opens this portal in a browser, which triggers the biometric prompt and signs the transaction using the user's passkey.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;configPaymaster&lt;/code&gt; - Paymaster Service Configuration
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;configPaymaster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
  &lt;span class="na"&gt;paymasterUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://kora.devnet.lazorkit.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Configures the paymaster service that sponsors transaction fees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paymaster URLs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Devnet: &lt;code&gt;https://kora.devnet.lazorkit.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mainnet: &lt;code&gt;https://kora.lazorkit.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You build and sign transaction client-side&lt;/li&gt;
&lt;li&gt;SDK automatically sends signed transaction to &lt;code&gt;paymasterUrl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Paymaster validates the transaction&lt;/li&gt;
&lt;li&gt;Paymaster adds its own fee payer instruction (sponsors the gas)&lt;/li&gt;
&lt;li&gt;Paymaster submits sponsored transaction to Solana RPC&lt;/li&gt;
&lt;li&gt;SDK receives transaction signature and returns to your app&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;No explicit API calls needed&lt;/strong&gt; - The Lazorkit SDK handles all paymaster communication internally when you call &lt;code&gt;wallet.signAndSendTransaction()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic Paymaster Routing
&lt;/h3&gt;

&lt;p&gt;Once configured, every transaction signed with &lt;code&gt;wallet.signAndSendTransaction()&lt;/code&gt; automatically routes through the paymaster. You don't need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make explicit paymaster API calls&lt;/li&gt;
&lt;li&gt;Handle paymaster request/response manually&lt;/li&gt;
&lt;li&gt;Add fee payer logic to your code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SDK does it all automatically based on the &lt;code&gt;configPaymaster.paymasterUrl&lt;/code&gt; setting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment-Specific Configuration
&lt;/h3&gt;

&lt;p&gt;For production apps, consider using environment variables:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LazorKitProvider&lt;/span&gt;
  &lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EXPO_PUBLIC_SOLANA_RPC_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;portalUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EXPO_PUBLIC_PORTAL_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;configPaymaster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
    &lt;span class="na"&gt;paymasterUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EXPO_PUBLIC_PAYMASTER_URL&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="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LazorKitProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows easy switching between Devnet and Mainnet without code changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Signing and Submitting the Transaction
&lt;/h2&gt;

&lt;p&gt;Now that the transaction is built and the paymaster is configured, let's implement the signing and submission flow using the Lazorkit SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Complete Signing Flow
&lt;/h3&gt;

&lt;p&gt;Here's the complete flow extracted from the confirm screen:&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;// Source: app/confirm.tsx:84-106&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleConfirmAndSign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="nf"&gt;setIsSubmitting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setTransactionStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Build USDC transfer transaction&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SolanaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buildUSDCTransfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;transactionRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Sign and send via Lazorkit SDK (triggers biometric + paymaster)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signAndSendTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;transactionOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;clusterSimulation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;devnet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passkey-integ://confirm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;setSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txSignature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Poll for confirmation (covered in Step 5)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SolanaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txSignature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ... handle status&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... handle error&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;h3&gt;
  
  
  Understanding signAndSendTransaction()
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;wallet.signAndSendTransaction()&lt;/code&gt; method is the heart of the Lazorkit SDK. Let's break down its parameters:&lt;/p&gt;

&lt;h4&gt;
  
  
  First Parameter: Transaction Payload
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;transactionOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;clusterSimulation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;devnet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;instructions&lt;/code&gt;&lt;/strong&gt;: Array of transaction instructions to sign&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extracted from the Transaction object built by SolanaService&lt;/li&gt;
&lt;li&gt;Contains the SPL Token transfer instruction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;transactionOptions.clusterSimulation&lt;/code&gt;&lt;/strong&gt;: Blockchain network for simulation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set to &lt;code&gt;'devnet'&lt;/code&gt; for development&lt;/li&gt;
&lt;li&gt;Set to &lt;code&gt;'mainnet-beta'&lt;/code&gt; for production&lt;/li&gt;
&lt;li&gt;Used by SDK to validate transaction before submission&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The Lazorkit SDK uses a specific API format. You pass &lt;code&gt;{ instructions: [...] }&lt;/code&gt;, NOT a complete Transaction object like standard Solana wallet adapters.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Second Parameter: Sign Options
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passkey-integ://confirm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;redirectUrl&lt;/code&gt;&lt;/strong&gt;: Deep link URL for returning to your app&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After signing, Portal redirects back to this URL&lt;/li&gt;
&lt;li&gt;Must match your app's deep link scheme (configured in &lt;code&gt;app.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Example: &lt;code&gt;'yourapp://confirm'&lt;/code&gt; or &lt;code&gt;'yourapp://callback'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is required for the authentication flow to return control to your app after biometric signing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Automatic Paymaster Flow
&lt;/h3&gt;

&lt;p&gt;When you call &lt;code&gt;wallet.signAndSendTransaction()&lt;/code&gt;, here's what happens automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────────┐
│                   Paymaster Flow (Automatic)                 │
└──────────────────────────────────────────────────────────────┘

1.  App calls wallet.signAndSendTransaction()
   └─&amp;gt; SDK receives unsigned transaction

2.  SDK triggers biometric prompt
   └─&amp;gt; User authenticates with Face ID/Touch ID

3.   SDK signs transaction with passkey
   └─&amp;gt; User signature applied to transfer instruction

4.  SDK sends signed transaction to paymaster
   └─&amp;gt; Automatic POST to configPaymaster.paymasterUrl

5.  Paymaster validates transaction
   └─&amp;gt; Checks signature, structure, and limits

6.  Paymaster adds fee payer instruction
   └─&amp;gt; Paymaster's wallet pays transaction fees (SOL)

7.  Paymaster submits to Solana RPC
   └─&amp;gt; Sponsored transaction sent to blockchain

8.  SDK receives transaction signature
   └─&amp;gt; Returns signature to your app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trustless Security Model
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Critical security feature:&lt;/strong&gt; Users only sign the transfer instruction, NOT the fee payment.&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;// What user signs:&lt;/span&gt;
&lt;span class="c1"&gt;//  "Transfer 10 USDC from my account to recipient"&lt;/span&gt;

&lt;span class="c1"&gt;// What user does NOT sign:&lt;/span&gt;
&lt;span class="c1"&gt;//  Fee payer instruction (added by paymaster after signing)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;User sees exactly what they're signing (transfer amount and recipient)&lt;/li&gt;
&lt;li&gt;Paymaster cannot modify the transfer instruction (already signed)&lt;/li&gt;
&lt;li&gt;Paymaster can only add its own fee payment (doesn't affect transfer)&lt;/li&gt;
&lt;li&gt;User always knows precisely what transaction will execute&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Biometric Authentication
&lt;/h3&gt;

&lt;p&gt;When &lt;code&gt;signAndSendTransaction()&lt;/code&gt; is called, the SDK automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Opens Lazorkit Portal in system browser&lt;/li&gt;
&lt;li&gt;Portal triggers OS biometric prompt (Face ID/Touch ID)&lt;/li&gt;
&lt;li&gt;User authenticates with biometrics&lt;/li&gt;
&lt;li&gt;Portal signs transaction using passkey credential&lt;/li&gt;
&lt;li&gt;Portal redirects back to your app via &lt;code&gt;redirectUrl&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;User cancellation:&lt;/strong&gt; If user cancels biometric prompt, SDK throws error:&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// User cancelled - handle gracefully&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;h3&gt;
  
  
  Error Handling
&lt;/h3&gt;

&lt;p&gt;Common errors during signing:&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;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signAndSendTransaction&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// User cancelled biometric prompt&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;not initialized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Recipient account doesn't exist&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Insufficient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Not enough USDC balance&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paymaster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Paymaster rejected transaction&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Other network or SDK errors&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;See Step 6 (Troubleshooting) for detailed error handling strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Confirming Transaction on Blockchain
&lt;/h2&gt;

&lt;p&gt;After the transaction is signed and submitted, you need to poll the blockchain to confirm it was successfully processed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transaction Confirmation Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Source: app/confirm.tsx:109-118&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txSignature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signAndSendTransaction&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="nf"&gt;setSignature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txSignature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Poll for confirmation with 'confirmed' commitment&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SolanaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txSignature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTransactionStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTransactionStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The confirmTransaction Method
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;SolanaService.confirmTransaction()&lt;/code&gt; method polls the RPC for transaction status:&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;// Source: services/SolanaService.ts:189-239&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionSignature&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TransactionStatus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getConnection&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;submittedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Poll for transaction confirmation with 'confirmed' commitment level&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&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;confirmedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if transaction succeeded or failed on-chain&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confirmation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Transaction was confirmed but failed execution&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;confirmations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Transaction failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confirmation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;explorerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SOLANA_EXPLORER_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;submittedAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;confirmedAt&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="c1"&gt;// Transaction succeeded&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;confirmations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;explorerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SOLANA_EXPLORER_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;submittedAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;confirmedAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Confirmation timeout or RPC error&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;confirmations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction confirmation failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;explorerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SOLANA_EXPLORER_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;submittedAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding Commitment Levels
&lt;/h3&gt;

&lt;p&gt;Solana offers different commitment levels for transaction confirmation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Commitment Level&lt;/th&gt;
&lt;th&gt;Confirmations&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Finality&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;processed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Real-time updates (NOT recommended for financial ops)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;confirmed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;15-20s&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;RECOMMENDED&lt;/strong&gt; - Good balance of speed and security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;finalized&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Max&lt;/td&gt;
&lt;td&gt;30+s&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;td&gt;Maximum security (slow for UX)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why we use 'confirmed':&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides high confidence of finality (32 confirmations)&lt;/li&gt;
&lt;li&gt;Much faster than 'finalized' (15-20s vs 30+s)&lt;/li&gt;
&lt;li&gt;Better user experience without sacrificing security&lt;/li&gt;
&lt;li&gt;Sufficient for most applications including financial transfers&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Once a transaction reaches 'confirmed' status, the probability of it being rolled back is extremely low on Solana.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The TransactionStatus Interface
&lt;/h3&gt;

&lt;p&gt;The confirmation method returns a &lt;code&gt;TransactionStatus&lt;/code&gt; object:&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;// Source: types/index.ts:85-131&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TransactionStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// Transaction signature&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;confirmations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// Number of confirmations (32 for 'confirmed')&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// Error message if failed&lt;/span&gt;
  &lt;span class="nl"&gt;explorerUrl&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// Solana Explorer link&lt;/span&gt;
  &lt;span class="nl"&gt;submittedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// Timestamp when submitted&lt;/span&gt;
  &lt;span class="nl"&gt;confirmedAt&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// Timestamp when confirmed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UI State Management
&lt;/h3&gt;

&lt;p&gt;The confirm screen manages transaction state through the lifecycle:&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;// Source: app/confirm.tsx:56-61&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;transactionStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTransactionStatus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preview&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preview&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;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSignature&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;State transitions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;preview&lt;/strong&gt; → User sees transaction details, taps "Confirm &amp;amp; Sign"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pending&lt;/strong&gt; → Show loading spinner while waiting for confirmation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;confirmed&lt;/strong&gt; → Show success checkmark with Explorer link&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;failed&lt;/strong&gt; → Show error message with retry option&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Displaying Transaction Status
&lt;/h3&gt;

&lt;p&gt;The UI shows different views based on transaction status:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pending State:&lt;/strong&gt;&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;// Source: app/confirm.tsx:242-256&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;transactionStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusContainer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Processing&lt;/span&gt; &lt;span class="nx"&gt;Transaction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ActivityIndicator&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;purple&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadingText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;Waiting&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;blockchain&lt;/span&gt; &lt;span class="nx"&gt;confirmation&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadingText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;may&lt;/span&gt; &lt;span class="nx"&gt;take&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Confirmed State:&lt;/strong&gt;&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;// Source: app/confirm.tsx:259-313&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;transactionStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;confirmed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusContainer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusIcon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Transaction&lt;/span&gt; &lt;span class="nx"&gt;Successful&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Show transaction details */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TouchableOpacity&lt;/span&gt; &lt;span class="nx"&gt;onPress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleViewOnExplorer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;Explorer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TouchableOpacity&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Opening Solana Explorer
&lt;/h3&gt;

&lt;p&gt;Allow users to view transaction details on Solana Explorer:&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;// Source: app/confirm.tsx:167-171&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleViewOnExplorer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;explorerUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://explorer.solana.com/tx/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?cluster=devnet`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;Linking&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;explorerUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Explorer shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transaction signature&lt;/li&gt;
&lt;li&gt;Block number and timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fee payer&lt;/strong&gt; (shows paymaster address, not user's address!)&lt;/li&gt;
&lt;li&gt;All instructions (transfer + fee payment)&lt;/li&gt;
&lt;li&gt;Transaction logs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 6: Verifying Gasless Execution
&lt;/h2&gt;

&lt;p&gt;One of the key benefits of the paymaster is that users don't need SOL to send USDC. Let's verify this.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Verify Gasless Transaction
&lt;/h3&gt;

&lt;p&gt;Follow these steps to confirm the transaction was truly gasless:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Check Wallet SOL Balance Before Transfer
&lt;/h4&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;Connection&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="s1"&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;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balanceBefore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOL balance before:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balanceBefore&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Complete USDC Transfer
&lt;/h4&gt;

&lt;p&gt;Execute the transfer as documented in Steps 1-5.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Check Wallet SOL Balance After Transfer
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balanceAfter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOL balance after:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;balanceAfter&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. Verify SOL Balance Unchanged
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balanceBefore&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;balanceAfter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction was gasless! SOL balance unchanged.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction consumed SOL:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;balanceBefore&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;balanceAfter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; SOL balance should be exactly the same (typically 0 SOL for new wallets).&lt;/p&gt;

&lt;h3&gt;
  
  
  What "Gasless" Means
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Gasless transactions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User's wallet never needs SOL for transaction fees&lt;/li&gt;
&lt;li&gt;Paymaster's wallet pays all gas fees (SOL)&lt;/li&gt;
&lt;li&gt;User only needs USDC to send USDC&lt;/li&gt;
&lt;li&gt;No friction for users to acquire SOL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Traditional transactions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User must hold SOL to pay gas fees&lt;/li&gt;
&lt;li&gt;Even for sending USDC, SOL is required&lt;/li&gt;
&lt;li&gt;Adds onboarding friction (where to buy SOL?)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Verifying on Solana Explorer
&lt;/h3&gt;

&lt;p&gt;Open the transaction in Solana Explorer to see the paymaster in action:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "View on Explorer" button after transaction confirms&lt;/li&gt;
&lt;li&gt;Look for the &lt;strong&gt;"Fee Payer"&lt;/strong&gt; field in transaction details&lt;/li&gt;
&lt;li&gt;The fee payer address will be the &lt;strong&gt;paymaster's wallet&lt;/strong&gt;, not your wallet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What to look for:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction Details
├─ Signature: 5VERv8NMvzb...
├─ Block: 234567890
├─ Fee Payer: &amp;lt;Paymaster Address&amp;gt;  ← Not your wallet!
├─ Recent Blockhash: abc123...
└─ Instructions:
    ├─ 1. SPL Token Transfer (your signature)
    └─ 2. Fee Payment (paymaster signature)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Fee Payer&lt;/strong&gt; field shows who paid for the transaction. In gasless transactions, this is the paymaster's address, proving your wallet didn't spend any SOL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits of Gasless Transactions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For Users:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simpler onboarding&lt;/strong&gt; - No need to acquire SOL first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-token model&lt;/strong&gt; - Only need USDC to send USDC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile-first UX&lt;/strong&gt; - Works like traditional payment apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant start&lt;/strong&gt; - Send transactions immediately after wallet creation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Developers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lower user friction&lt;/strong&gt; - Higher conversion rates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better UX&lt;/strong&gt; - No "acquire gas" tutorial needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mainstream appeal&lt;/strong&gt; - Blockchain complexity hidden&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business model flexibility&lt;/strong&gt; - You control sponsorship limits&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Common issues you may encounter when implementing gasless USDC transfers:&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 1: "Recipient USDC token account not initialized"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recipient USDC token account not initialized. They must receive USDC at least once before you can send to them.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Recipient has never received USDC before&lt;/li&gt;
&lt;li&gt;Their Associated Token Account (ATA) for USDC doesn't exist on-chain&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Recipient must receive USDC at least once from another source first&lt;/li&gt;
&lt;li&gt;Options: Devnet faucet, exchange transfer, or transfer from another wallet&lt;/li&gt;
&lt;li&gt;The paymaster will NOT initialize token accounts (too expensive and abusable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prevention:&lt;/strong&gt;&lt;br&gt;
Display a clear error message to users explaining that the recipient needs to initialize their USDC account first.&lt;/p&gt;


&lt;h3&gt;
  
  
  Issue 2: "TokenOwnerOffCurveError when calling getAssociatedTokenAddress"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TokenOwnerOffCurveError: Token owner off curve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Missing &lt;code&gt;allowOwnerOffCurve: true&lt;/code&gt; parameter in &lt;code&gt;getAssociatedTokenAddress()&lt;/code&gt; call&lt;/li&gt;
&lt;li&gt;Lazorkit smart wallets use Program Derived Addresses (PDAs) which are off-curve&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Always add the third parameter to &lt;code&gt;getAssociatedTokenAddress()&lt;/code&gt;:&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;// WRONG - Will fail for smart wallets&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT - Works with smart wallets&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Background:&lt;/strong&gt;&lt;br&gt;
PDAs are intentionally "off the ed25519 curve" - they're deterministic addresses generated using seeds, not random keypairs. The &lt;code&gt;allowOwnerOffCurve&lt;/code&gt; parameter tells the SDK to allow these addresses.&lt;/p&gt;


&lt;h3&gt;
  
  
  Issue 3: "Transaction signing was cancelled"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction signing was cancelled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;User cancelled the biometric prompt (Face ID/Touch ID)&lt;/li&gt;
&lt;li&gt;User dismissed the authentication dialog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
This is expected user behavior - handle gracefully:&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Show user-friendly message&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Transaction signing was cancelled. You can try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User Experience:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow users to retry the transaction&lt;/li&gt;
&lt;li&gt;Don't treat cancellation as a critical error&lt;/li&gt;
&lt;li&gt;Provide a "Try Again" button&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Issue 4: "Insufficient USDC balance"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You don't have enough USDC to complete this transfer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;User's wallet doesn't have enough USDC for the transfer amount&lt;/li&gt;
&lt;li&gt;Balance check happens on-chain when transaction executes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
Implement client-side balance validation before transaction building:&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;// Query USDC balance before building transaction&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SOLANA_RPC_URL&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;tokenAccount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAssociatedTokenAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;USDC_MINT_DEVNET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTokenAccountBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenAccount&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;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;accountInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uiAmount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// USDC balance as decimal&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Insufficient USDC balance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Display wallet balance on transfer form&lt;/li&gt;
&lt;li&gt;Validate &lt;code&gt;amount &amp;lt;= balance&lt;/code&gt; before allowing submission&lt;/li&gt;
&lt;li&gt;Provide a "Max" button to set amount to full balance&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Issue 5: "Transaction confirmation timeout"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction confirmation failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Solana network congestion&lt;/li&gt;
&lt;li&gt;RPC node issues or downtime&lt;/li&gt;
&lt;li&gt;Transaction took longer than expected to confirm&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Option 1: Retry confirmation polling&lt;/strong&gt;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SolanaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timeout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Retry confirmation&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;retryStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SolanaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;confirmTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Check Explorer manually&lt;/strong&gt;&lt;br&gt;
Even if confirmation times out, the transaction may have succeeded. Check Solana Explorer with the transaction signature.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Implement exponential backoff retry logic&lt;/li&gt;
&lt;li&gt;Extend confirmation timeout for congested periods&lt;/li&gt;
&lt;li&gt;Use a more reliable RPC provider (QuickNode, Alchemy, Helius)&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Issue 6: "Paymaster rejected transaction"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction could not be sponsored. Please try again later.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Transaction exceeds paymaster size limits&lt;/li&gt;
&lt;li&gt;Paymaster service is temporarily down&lt;/li&gt;
&lt;li&gt;Transaction structure is invalid&lt;/li&gt;
&lt;li&gt;Paymaster rate limits exceeded&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;For size limits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep transactions simple (single transfer instruction)&lt;/li&gt;
&lt;li&gt;Don't add unnecessary instructions or metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For service downtime:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement retry logic with exponential backoff&lt;/li&gt;
&lt;li&gt;Show user-friendly error: "Service temporarily unavailable"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For invalid transactions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate all transaction parameters before signing&lt;/li&gt;
&lt;li&gt;Ensure blockhash is fresh (&amp;lt; 60 seconds old)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Devnet Note:&lt;/strong&gt;&lt;br&gt;
The Devnet paymaster may have stricter limits than Mainnet. This is normal for testing environments.&lt;/p&gt;


&lt;h3&gt;
  
  
  Issue 7: "Stale blockhash error"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error Message:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transaction simulation failed: Blockhash not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Transaction not submitted within ~60 seconds of fetching blockhash&lt;/li&gt;
&lt;li&gt;Blockhash expired before reaching the blockchain&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Always fetch blockhash immediately before signing:&lt;/strong&gt;&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;// WRONG - Building transaction minutes in advance&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Gets blockhash&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;doSomethingElse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                      &lt;span class="c1"&gt;// Time passes...&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// Blockhash is stale!&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT - Build and sign immediately&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Gets fresh blockhash&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// Sign right away&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Build transactions just-in-time (right before signing)&lt;/li&gt;
&lt;li&gt;Don't pre-build transactions and store them&lt;/li&gt;
&lt;li&gt;If you must wait, fetch a new blockhash before signing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Blockhash Lifetime:&lt;/strong&gt;&lt;br&gt;
Blockhashes are valid for approximately 60 seconds (150 blocks at 400ms per block).&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing Your Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manual Testing Checklist
&lt;/h3&gt;

&lt;p&gt;Follow these steps to manually test your gasless USDC transfer implementation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Setup Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Wallet created and authenticated (Tutorial 1 complete)&lt;/li&gt;
&lt;li&gt;[ ] App connected to Devnet&lt;/li&gt;
&lt;li&gt;[ ] Deep linking configured correctly&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Get Test USDC&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prepare Test Recipient&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Find or create a test recipient address&lt;/li&gt;
&lt;li&gt;[ ] Ensure recipient has initialized USDC account (received USDC at least once)&lt;/li&gt;
&lt;li&gt;[ ] If recipient is new, send them test USDC from faucet first&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Transfer Flow&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Open transfer form&lt;/li&gt;
&lt;li&gt;[ ] Enter valid recipient address&lt;/li&gt;
&lt;li&gt;[ ] Enter amount (less than balance)&lt;/li&gt;
&lt;li&gt;[ ] Verify "Review Transaction" button enables&lt;/li&gt;
&lt;li&gt;[ ] Tap "Review Transaction"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Transaction Preview&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Verify recipient address displays correctly (truncated)&lt;/li&gt;
&lt;li&gt;[ ] Verify amount displays correctly&lt;/li&gt;
&lt;li&gt;[ ] Verify "Gasless Transaction" badge appears&lt;/li&gt;
&lt;li&gt;[ ] Tap "Confirm &amp;amp; Sign"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Biometric Signing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Biometric prompt appears (Face ID/Touch ID)&lt;/li&gt;
&lt;li&gt;[ ] Authenticate with biometrics&lt;/li&gt;
&lt;li&gt;[ ] Browser opens with Lazorkit Portal (briefly)&lt;/li&gt;
&lt;li&gt;[ ] App receives control back via deep link&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Transaction Confirmation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] "Pending" screen displays with loading spinner&lt;/li&gt;
&lt;li&gt;[ ] Wait 15-30 seconds for confirmation&lt;/li&gt;
&lt;li&gt;[ ] "Success" screen displays with checkmark&lt;/li&gt;
&lt;li&gt;[ ] Transaction signature displays&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify Gasless Execution&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Check SOL balance unchanged (should be 0 or same as before)&lt;/li&gt;
&lt;li&gt;[ ] Tap "View on Explorer"&lt;/li&gt;
&lt;li&gt;[ ] Verify Fee Payer is paymaster address (not your wallet)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Test Error Cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Test with invalid recipient address (should show validation error)&lt;/li&gt;
&lt;li&gt;[ ] Test with insufficient balance (should show error)&lt;/li&gt;
&lt;li&gt;[ ] Test with uninitialized recipient (should show "not initialized" error)&lt;/li&gt;
&lt;li&gt;[ ] Test cancelling biometric prompt (should show cancellation error)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Using Solana Explorer
&lt;/h3&gt;

&lt;p&gt;After transaction confirms, verify details on Solana Explorer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Open Explorer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "View on Explorer" button in app&lt;/li&gt;
&lt;li&gt;Or manually visit: &lt;code&gt;https://explorer.solana.com/tx/YOUR_SIGNATURE?cluster=devnet&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify Transaction Details&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt;: Transaction unique identifier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block&lt;/strong&gt;: Block number where transaction was included&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp&lt;/strong&gt;: When transaction was confirmed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fee Payer&lt;/strong&gt;: Should be paymaster address (NOT your wallet!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Status&lt;/strong&gt;: Should show "Success"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Inspect Instructions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instruction 1&lt;/strong&gt;: SPL Token Transfer (your transfer)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instruction 2&lt;/strong&gt;: Fee Payment (paymaster's fee payment)&lt;/li&gt;
&lt;li&gt;Verify transfer amount matches what you sent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check Fee&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transaction fee (usually ~0.000005 SOL)&lt;/li&gt;
&lt;li&gt;Verify paid by paymaster, not your wallet&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Congratulations! You've successfully implemented gasless USDC transfers with Lazorkit. &lt;/p&gt;

&lt;h3&gt;
  
  
  Production Considerations
&lt;/h3&gt;

&lt;p&gt;Before launching to production:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Switch to Mainnet&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update RPC URL to Mainnet: &lt;code&gt;https://api.mainnet-beta.solana.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update paymaster URL: &lt;code&gt;https://kora.lazorkit.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update USDC mint to Mainnet USDC address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use Paid RPC Provider&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public RPCs have rate limits (~40 req/10s)&lt;/li&gt;
&lt;li&gt;Consider QuickNode, Alchemy, or Helius&lt;/li&gt;
&lt;li&gt;Implement retry logic for RPC failures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add Error Tracking&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrate Sentry or similar error tracking&lt;/li&gt;
&lt;li&gt;Log all transaction errors with context&lt;/li&gt;
&lt;li&gt;Monitor paymaster rejection rates&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implement Analytics&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track transaction success/failure rates&lt;/li&gt;
&lt;li&gt;Monitor average confirmation times&lt;/li&gt;
&lt;li&gt;Measure user drop-off points in flow&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add Security Features&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement transaction amount limits&lt;/li&gt;
&lt;li&gt;Add address whitelist/blacklist functionality&lt;/li&gt;
&lt;li&gt;Consider adding 2FA for large transactions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Related Tutorials
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ola-zoll/wallet-creation-with-biometric-authentication-1j6l"&gt;Tutorial 1: Wallet Creation with Biometric Authentication&lt;/a&gt; - Review wallet setup&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Additional Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.lazorkit.com" rel="noopener noreferrer"&gt;Lazorkit SDK Documentation&lt;/a&gt; - Official SDK reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://solana-labs.github.io/solana-web3.js/" rel="noopener noreferrer"&gt;Solana Web3.js Documentation&lt;/a&gt; - Transaction building&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://spl.solana.com/token" rel="noopener noreferrer"&gt;SPL Token Documentation&lt;/a&gt; - Token accounts and transfers&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://explorer.solana.com/" rel="noopener noreferrer"&gt;Solana Explorer&lt;/a&gt; - View transactions (Devnet and Mainnet)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://solanacookbook.com/" rel="noopener noreferrer"&gt;Solana Cookbook&lt;/a&gt; - Practical Solana development guides&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://webauthn.guide/" rel="noopener noreferrer"&gt;WebAuthn Guide&lt;/a&gt; - Understanding passkey authentication&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You've learned how to implement gasless USDC transfers using Lazorkit's paymaster integration. Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPL Token transfers require Associated Token Accounts (ATAs) for sender and recipient&lt;/li&gt;
&lt;li&gt;Smart wallets require &lt;code&gt;allowOwnerOffCurve: true&lt;/code&gt; when deriving token accounts&lt;/li&gt;
&lt;li&gt;Paymaster automatically sponsors gas fees when configured in LazorKitProvider&lt;/li&gt;
&lt;li&gt;Users sign only the transfer instruction (trustless security model)&lt;/li&gt;
&lt;li&gt;Transaction confirmation uses 'confirmed' commitment (15-20s, good UX)&lt;/li&gt;
&lt;li&gt;Gasless execution means users need zero SOL to send USDC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By removing the gas fee requirement, you've created a significantly better user experience that removes blockchain friction and feels like traditional payment apps.&lt;/p&gt;

&lt;p&gt;Happy building! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>blockchain</category>
      <category>web3</category>
      <category>solana</category>
    </item>
    <item>
      <title>Wallet Creation with Biometric Authentication</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Tue, 13 Jan 2026 23:38:31 +0000</pubDate>
      <link>https://forem.com/ola-zoll/wallet-creation-with-biometric-authentication-1j6l</link>
      <guid>https://forem.com/ola-zoll/wallet-creation-with-biometric-authentication-1j6l</guid>
      <description>&lt;p&gt;Welcome to my hands-on tutorial on implementing biometric wallet authentication using the Lazorkit SDK! &lt;/p&gt;

&lt;p&gt;In this tutorial, you'll learn how to implement a seamless wallet creation experience powered by WebAuthn passkeys—no seed phrases, no passwords, just your fingerprint or Face ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What You'll Build:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By the end of this tutorial, you'll have implemented a complete biometric wallet creation flow that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opens a secure authentication portal in the user's browser&lt;/li&gt;
&lt;li&gt;Prompts for Face ID (iOS) or fingerprint authentication (Android)&lt;/li&gt;
&lt;li&gt;Creates a Solana wallet bound to the user's biometric credential&lt;/li&gt;
&lt;li&gt;Handles deep link callbacks to return the user to your app&lt;/li&gt;
&lt;li&gt;Persists the wallet session for automatic reconnection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What You'll Learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing and configuring the Lazorkit SDK with required polyfills&lt;/li&gt;
&lt;li&gt;Setting up deep linking for authentication callbacks&lt;/li&gt;
&lt;li&gt;Configuring the LazorKitProvider with Devnet endpoints&lt;/li&gt;
&lt;li&gt;Building a wallet creation UI with the &lt;code&gt;useWallet&lt;/code&gt; hook&lt;/li&gt;
&lt;li&gt;Handling authentication flow and navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time Required:&lt;/strong&gt; ~20-30 minutes&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Native knowledge&lt;/strong&gt;: Familiarity with React hooks, components, and navigation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo CLI installed&lt;/strong&gt;: Install with &lt;code&gt;npm install -g expo-cli&lt;/code&gt; if needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development environment&lt;/strong&gt;: iOS (Xcode) or Android (Android Studio) development tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical device with biometrics&lt;/strong&gt;: Face ID, Touch ID, or fingerprint sensor (biometrics don't work reliably on simulators)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo SDK 54&lt;/strong&gt;: This tutorial assumes Expo SDK 54 or later&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Install dependencies
&lt;/h2&gt;

&lt;p&gt;First, install the Lazorkit SDK and required polyfills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the Lazorkit SDK
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @lazorkit/wallet-mobile-adapter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install required polyfills
&lt;/h3&gt;

&lt;p&gt;The Lazorkit SDK depends on several JavaScript APIs that aren't available in React Native by default. Install these polyfills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-native-get-random-values react-native-url-polyfill buffer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Package Versions&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@lazorkit/wallet-mobile-adapter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;^1.5.1&lt;/td&gt;
&lt;td&gt;Core SDK for passkey authentication and Solana wallet management&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@solana/web3.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;^1.98.4&lt;/td&gt;
&lt;td&gt;Solana blockchain interactions (installed as SDK dependency)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-native-get-random-values&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.11.0&lt;/td&gt;
&lt;td&gt;Crypto randomness polyfill (CRITICAL - provides &lt;code&gt;crypto.getRandomValues()&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;react-native-url-polyfill&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;^3.0.0&lt;/td&gt;
&lt;td&gt;URL API polyfill (CRITICAL - provides &lt;code&gt;URL&lt;/code&gt; global for RPC and deep linking)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;buffer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;^6.0.3&lt;/td&gt;
&lt;td&gt;Buffer polyfill (CRITICAL - provides &lt;code&gt;Buffer&lt;/code&gt; global for Solana binary operations)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Why These Polyfills Are Required
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;react-native-get-random-values&lt;/strong&gt;: The Solana SDK uses &lt;code&gt;crypto.getRandomValues()&lt;/code&gt; for cryptographic operations (keypair generation, transaction signing). React Native doesn't provide this API natively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;react-native-url-polyfill&lt;/strong&gt;: The Solana RPC client and deep linking logic use the &lt;code&gt;URL&lt;/code&gt; class for parsing endpoints and callback URLs. React Native's JavaScriptCore doesn't include the full URL API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;buffer&lt;/strong&gt;: Solana transactions are binary data structures that use Node.js &lt;code&gt;Buffer&lt;/code&gt; objects. React Native doesn't provide &lt;code&gt;Buffer&lt;/code&gt; globally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compatibility note&lt;/strong&gt;: This tutorial uses Expo SDK 54. Ensure your Expo version is compatible with these polyfill versions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Configure polyfills
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CRITICAL&lt;/strong&gt;: Polyfills MUST be imported at the VERY TOP of your root layout file, before ANY other imports. The Solana SDK will crash if these APIs aren't available when it loads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Polyfill Imports
&lt;/h3&gt;

&lt;p&gt;Open (or create) your root layout file at &lt;code&gt;app/_layout.tsx&lt;/code&gt; and add these imports as the &lt;strong&gt;first lines&lt;/strong&gt; of the file:&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;// CRITICAL: Import polyfills FIRST, in this exact order&lt;/span&gt;
&lt;span class="c1"&gt;// Note: react-native-get-random-values is imported by @lazorkit/wallet-mobile-adapter internally&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-url-polyfill/auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// MUST be first - provides URL API for Solana RPC and deep linking&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Buffer&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="s1"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// MUST be second - provides Buffer for Solana binary operations&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Assign Buffer to global namespace&lt;/span&gt;

&lt;span class="c1"&gt;// Now safe to import other modules after polyfills are loaded&lt;/span&gt;
&lt;span class="c1"&gt;// eslint-disable-next-line import/first -- Polyfills must be imported before other modules&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LazorKitProvider&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="s1"&gt;@lazorkit/wallet-mobile-adapter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ... other imports&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Import Order Matters
&lt;/h3&gt;

&lt;p&gt;The Solana SDK and Lazorkit adapter access these globals (&lt;code&gt;crypto&lt;/code&gt;, &lt;code&gt;URL&lt;/code&gt;, &lt;code&gt;Buffer&lt;/code&gt;) during module initialization. If you import the SDK before the polyfills, you'll get errors like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TypeError: Cannot read property 'getRandomValues' of undefined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReferenceError: Buffer is not defined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ReferenceError: URL is not defined&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;react-native-url-polyfill&lt;/strong&gt; must be imported first (the SDK imports react-native-get-random-values internally)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buffer&lt;/strong&gt; must be assigned to &lt;code&gt;global.Buffer&lt;/code&gt; so Solana SDK can find it&lt;/li&gt;
&lt;li&gt;All polyfills must load &lt;strong&gt;before&lt;/strong&gt; any Solana or Lazorkit imports&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Common Mistake&lt;/strong&gt;: Adding polyfills in the middle of your import list. They won't work if the SDK has already been loaded!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Configure deep linking
&lt;/h2&gt;

&lt;p&gt;Deep linking allows the Lazorkit Portal to redirect back to your app after authentication. You must configure a unique URL scheme in your &lt;code&gt;app.json&lt;/code&gt; configuration file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Deep Linking Configuration to &lt;code&gt;app.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Open your &lt;code&gt;app.json&lt;/code&gt; file and add the following fields inside the &lt;code&gt;expo&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scheme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lazorkitstarter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bundleIdentifier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.lazorkitstarter.app"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.lazorkitstarter.app"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Deep Linking Is
&lt;/h3&gt;

&lt;p&gt;Deep linking is a mechanism that allows URLs to open specific screens in your mobile app. When a user authenticates in the Lazorkit Portal (which opens in their browser), the portal redirects back to your app using a deep link like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lazorkitstarter://callback?token=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operating system sees this URL, recognizes the &lt;code&gt;lazorkitstarter://&lt;/code&gt; scheme, and opens your app at the callback route.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration Fields Explained
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;scheme&lt;/code&gt;&lt;/strong&gt;: Your app's unique deep link URL scheme. This should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unique (avoid conflicts with other apps)&lt;/li&gt;
&lt;li&gt;Lowercase (iOS requirement)&lt;/li&gt;
&lt;li&gt;No special characters or spaces&lt;/li&gt;
&lt;li&gt;Match the scheme you'll use in &lt;code&gt;LazorKitProvider&lt;/code&gt; configuration&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;ios.bundleIdentifier&lt;/code&gt;&lt;/strong&gt;: Your iOS app's unique identifier. This must match your Apple Developer account configuration.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;android.package&lt;/code&gt;&lt;/strong&gt;: Your Android app's package name. This must match your Google Play Console configuration.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choosing a Unique Scheme
&lt;/h3&gt;

&lt;p&gt;Choose a scheme name that's unlikely to conflict with other apps. Good patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your company name + "app": &lt;code&gt;acmecorpapp://&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Your app name: &lt;code&gt;mywallet://&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A unique prefix: &lt;code&gt;mycompany-wallet://&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: After changing &lt;code&gt;app.json&lt;/code&gt;, you must rebuild your app (changes don't apply via over-the-air updates).&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Deep Links
&lt;/h3&gt;

&lt;p&gt;You can test that your deep linking configuration works with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# iOS&lt;/span&gt;
npx uri-scheme open lazorkitstarter://test &lt;span class="nt"&gt;--ios&lt;/span&gt;

&lt;span class="c"&gt;# Android&lt;/span&gt;
npx uri-scheme open lazorkitstarter://test &lt;span class="nt"&gt;--android&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should open your app if deep linking is configured correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Setup LazorKitProvider
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;LazorKitProvider&lt;/code&gt; is a React Context provider that wraps your entire app and provides wallet functionality to all child components. You'll configure it with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Solana RPC endpoint (Devnet for development)&lt;/li&gt;
&lt;li&gt;The Lazorkit Portal URL (for WebAuthn authentication)&lt;/li&gt;
&lt;li&gt;A Paymaster URL (for gasless transactions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configure LazorKitProvider in &lt;code&gt;app/_layout.tsx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;app/_layout.tsx&lt;/code&gt; file (after the polyfill imports), wrap your navigation stack with &lt;code&gt;LazorKitProvider&lt;/code&gt;:&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;// CRITICAL: Import polyfills FIRST (see Step 2)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-url-polyfill/auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Buffer&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="s1"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Now safe to import other modules after polyfills are loaded&lt;/span&gt;
&lt;span class="c1"&gt;// eslint-disable-next-line import/first -- Polyfills must be imported before other modules&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LazorKitProvider&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="s1"&gt;@lazorkit/wallet-mobile-adapter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// eslint-disable-next-line import/first -- Polyfills must be imported before other modules&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Stack&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="s1"&gt;expo-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LazorKitProvider&lt;/span&gt;
      &lt;span class="nx"&gt;rpcUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.devnet.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// Solana Devnet RPC endpoint for blockchain interactions&lt;/span&gt;
      &lt;span class="nx"&gt;portalUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://portal.lazor.sh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// Lazorkit Portal for WebAuthn passkey authentication&lt;/span&gt;
      &lt;span class="nx"&gt;configPaymaster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;paymasterUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://kora.devnet.lazorkit.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Lazorkit Paymaster for gasless transactions&lt;/span&gt;
      &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;callback&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;headerShown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Wallet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Stack&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/LazorKitProvider&lt;/span&gt;&lt;span class="err"&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;h3&gt;
  
  
  LazorKitProvider Props Explained
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;rpcUrl&lt;/code&gt;&lt;/strong&gt; (required):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Solana RPC endpoint your app will use for blockchain interactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Devnet&lt;/strong&gt;: &lt;code&gt;https://api.devnet.solana.com&lt;/code&gt; (for development/testing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mainnet&lt;/strong&gt;: &lt;code&gt;https://api.mainnet-beta.solana.com&lt;/code&gt; (for production)&lt;/li&gt;
&lt;li&gt;Used for: Balance queries, transaction submission, confirmation polling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;portalUrl&lt;/code&gt;&lt;/strong&gt; (required):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Lazorkit Portal URL that handles WebAuthn authentication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production&lt;/strong&gt;: &lt;code&gt;https://portal.lazor.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The portal manages the WebAuthn credential creation ceremony and presents the biometric prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;configPaymaster.paymasterUrl&lt;/code&gt;&lt;/strong&gt; (required):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The paymaster service that sponsors transaction gas fees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Devnet&lt;/strong&gt;: &lt;code&gt;https://kora.devnet.lazorkit.com&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mainnet&lt;/strong&gt;: Contact Lazorkit for mainnet paymaster access&lt;/li&gt;
&lt;li&gt;Allows users to send transactions without holding SOL for gas fees&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How the Provider Works
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;LazorKitProvider&lt;/code&gt; creates a React Context that provides the &lt;code&gt;useWallet()&lt;/code&gt; hook to all child components. This hook gives you access to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wallet.connect()&lt;/code&gt; - Start authentication flow&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wallet.disconnect()&lt;/code&gt; - Clear wallet session&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wallet.isConnected&lt;/code&gt; - Check connection status&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wallet.isConnecting&lt;/code&gt; - Check if authentication is in progress&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wallet.smartWalletPubkey&lt;/code&gt; - Get user's Solana public key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wallet.signAndSendTransaction()&lt;/code&gt; - Sign and submit transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note on Mainnet vs Devnet&lt;/strong&gt;: For production apps, change &lt;code&gt;rpcUrl&lt;/code&gt; to mainnet and ensure you have a mainnet paymaster configured. Everything else stays the same.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Implement Wallet Creation UI
&lt;/h2&gt;

&lt;p&gt;Now that the SDK is configured, let's build the UI that lets users create a wallet with biometrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Welcome Screen with Wallet Creation Button
&lt;/h3&gt;

&lt;p&gt;Create (or update) &lt;code&gt;app/index.tsx&lt;/code&gt; with the following code:&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;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TouchableOpacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ActivityIndicator&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="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRouter&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="s1"&gt;expo-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useWallet&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="s1"&gt;@lazorkit/wallet-mobile-adapter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;WelcomeScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useWallet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Access wallet context from LazorKitProvider&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouter&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;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Handle wallet creation button press&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleCreateWallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Clear any previous errors&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Initiate wallet creation - SDK will open Lazorkit Portal in browser&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazorkitstarter://callback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// MUST match scheme in app.json&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Wallet connection error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Set user-friendly error message based on error type&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;abort&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authentication cancelled. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;network&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timeout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network error. Please check your connection and try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create wallet. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Auto-navigate to home screen after successful wallet connection&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Lazorkit&lt;/span&gt; &lt;span class="nx"&gt;Mobile&lt;/span&gt; &lt;span class="nx"&gt;Wallet&lt;/span&gt; &lt;span class="nx"&gt;Starter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Experience&lt;/span&gt; &lt;span class="nx"&gt;seamless&lt;/span&gt; &lt;span class="nx"&gt;biometric&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="nx"&gt;authentication&lt;/span&gt; &lt;span class="nx"&gt;powered&lt;/span&gt; &lt;span class="nx"&gt;by&lt;/span&gt; &lt;span class="nx"&gt;WebAuthn&lt;/span&gt; &lt;span class="nx"&gt;passkeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
          &lt;span class="nx"&gt;No&lt;/span&gt; &lt;span class="nx"&gt;seed&lt;/span&gt; &lt;span class="nx"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;no&lt;/span&gt; &lt;span class="nx"&gt;passwords&lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="nx"&gt;just&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;fingerprint&lt;/span&gt; &lt;span class="nx"&gt;or&lt;/span&gt; &lt;span class="nx"&gt;face&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TouchableOpacity&lt;/span&gt;
          &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnecting&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buttonDisabled&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
          &lt;span class="nx"&gt;onPress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCreateWallet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnecting&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;accessibilityRole&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;accessibilityLabel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create wallet using biometric authentication&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnecting&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadingContainer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ActivityIndicator&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Connecting&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&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="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Create&lt;/span&gt; &lt;span class="nx"&gt;Wallet&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;Biometrics&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TouchableOpacity&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errorText&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StyleSheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FAFAFA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;paddingHorizontal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;700&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#171717&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#171717&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#9945FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Solana purple&lt;/span&gt;
    &lt;span class="na"&gt;paddingVertical&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;paddingHorizontal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;buttonDisabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;loadingContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;buttonText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;600&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;errorText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#EF4444&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  useWallet Hook Properties and Methods
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;useWallet()&lt;/code&gt; hook returns an object with the following properties:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State Properties:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet.isConnected&lt;/code&gt;&lt;/strong&gt;: Boolean indicating if the wallet is currently connected&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;true&lt;/code&gt; after successful authentication&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;false&lt;/code&gt; before authentication or after disconnect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRITICAL&lt;/strong&gt;: Use &lt;code&gt;isConnected&lt;/code&gt;, NOT &lt;code&gt;connected&lt;/code&gt; &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet.isConnecting&lt;/code&gt;&lt;/strong&gt;: Boolean indicating if authentication is in progress&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;true&lt;/code&gt; while SDK is opening portal and waiting for callback&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;false&lt;/code&gt; when idle or after completion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRITICAL&lt;/strong&gt;: Use &lt;code&gt;isConnecting&lt;/code&gt;, NOT &lt;code&gt;connecting&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet.smartWalletPubkey&lt;/code&gt;&lt;/strong&gt;: PublicKey object or &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains the user's Solana wallet address after successful connection&lt;/li&gt;
&lt;li&gt;Type: &lt;code&gt;PublicKey&lt;/code&gt; from &lt;code&gt;@solana/web3.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Access string representation with: &lt;code&gt;wallet.smartWalletPubkey.toString()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRITICAL&lt;/strong&gt;: Use &lt;code&gt;smartWalletPubkey&lt;/code&gt;, NOT &lt;code&gt;publicKey&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet.connect(options)&lt;/code&gt;&lt;/strong&gt;: Initiates the authentication flow&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opens Lazorkit Portal in system browser&lt;/li&gt;
&lt;li&gt;Portal triggers WebAuthn biometric prompt&lt;/li&gt;
&lt;li&gt;Returns a Promise that resolves when authentication completes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Options&lt;/strong&gt;: &lt;code&gt;{ redirectUrl: 'your-scheme://callback' }&lt;/code&gt; (must match app.json scheme)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet.disconnect()&lt;/code&gt;&lt;/strong&gt;: Clears wallet session and disconnects&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets &lt;code&gt;isConnected&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Clears &lt;code&gt;smartWalletPubkey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Usually called when user wants to log out&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;wallet.signAndSendTransaction(transaction)&lt;/code&gt;&lt;/strong&gt;: Signs and submits a transaction &lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Critical SDK API Naming Convention
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: The Lazorkit SDK uses specific property names that differ from some documentation examples:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Correct&lt;/th&gt;
&lt;th&gt;Wrong&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wallet.isConnected&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wallet.connected&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wallet.isConnecting&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wallet.connecting&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wallet.smartWalletPubkey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;wallet.publicKey&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Using the wrong property names will cause TypeScript errors and runtime failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  User Flow Explanation
&lt;/h3&gt;

&lt;p&gt;When the user taps "Create Wallet with Biometrics":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;handleCreateWallet()&lt;/code&gt; calls &lt;code&gt;wallet.connect({ redirectUrl: '...' })&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SDK opens Lazorkit Portal in the system browser (via &lt;code&gt;expo-web-browser&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Portal initiates WebAuthn credential creation&lt;/li&gt;
&lt;li&gt;Native OS prompts for biometric authentication (Face ID/Touch ID/Fingerprint)&lt;/li&gt;
&lt;li&gt;User authenticates with biometrics&lt;/li&gt;
&lt;li&gt;Portal creates Solana wallet bound to the passkey&lt;/li&gt;
&lt;li&gt;Portal redirects back via deep link: &lt;code&gt;lazorkitstarter://callback?token=...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SDK processes callback, updates &lt;code&gt;wallet.isConnected&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, sets &lt;code&gt;wallet.smartWalletPubkey&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;React useEffect detects state change and navigates to home screen&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 6: Handle Authentication Callback
&lt;/h2&gt;

&lt;p&gt;After the user authenticates in the Lazorkit Portal, the SDK needs to detect when the wallet connection succeeds and navigate the user to the appropriate screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic Navigation with useEffect
&lt;/h3&gt;

&lt;p&gt;The wallet state changes are handled by the &lt;code&gt;useEffect&lt;/code&gt; hook in the Welcome screen:&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;// Auto-navigate to home screen after successful wallet connection&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Navigate to home screen&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="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authentication Callback Flow
&lt;/h3&gt;

&lt;p&gt;Here's the complete sequence of events during authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────┐
│ 1. User taps│
│   "Create   │
│   Wallet"   │
└──────┬──────┘
       │
       ▼
┌──────────────────────────────────┐
│ 2. wallet.connect() called       │
│    SDK opens Lazorkit Portal     │
│    in system browser             │
└──────┬───────────────────────────┘
       │
       ▼
┌──────────────────────────────────┐
│ 3. Portal initiates WebAuthn     │
│    credential creation ceremony  │
└──────┬───────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│ 4. Native biometric prompt      │
│    appears (Face ID/Touch ID)   │
└──────┬──────────────────────────┘
       │
       ▼
┌──────────────────────────────────┐
│ 5. User authenticates            │
│    OS creates passkey credential │
└──────┬───────────────────────────┘
       │
       ▼
┌──────────────────────────────────┐
│ 6. Portal generates Solana       │
│    wallet bound to passkey       │
└──────┬───────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│ 7. Portal redirects via deep    │
│    link: scheme://callback?token│
└──────┬──────────────────────────┘
       │
       ▼
┌─────────────────────────────────┐
│ 8. OS opens app, SDK receives   │
│    deep link and processes token│
└──────┬──────────────────────────┘
       │
       ▼
┌──────────────────────────────────┐
│ 9. SDK updates wallet state:     │
│    - isConnected = true          │
│    - smartWalletPubkey = address │
└──────┬───────────────────────────┘
       │
       ▼
┌──────────────────────────────────┐
│ 10. useEffect detects state      │
│     change and navigates to      │
│     home screen                  │
└──────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Points About the Callback
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Deep Link Processing&lt;/strong&gt;: The SDK automatically handles the deep link callback. You don't need to manually parse the token or process the callback URL. The SDK:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listens for deep links matching your configured scheme&lt;/li&gt;
&lt;li&gt;Extracts the authentication token from the URL&lt;/li&gt;
&lt;li&gt;Validates the token with the Lazorkit backend&lt;/li&gt;
&lt;li&gt;Updates the wallet context state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;State Updates&lt;/strong&gt;: When the callback is processed, the SDK sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wallet.isConnected = true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wallet.smartWalletPubkey = [PublicKey object]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Navigation&lt;/strong&gt;: Your React component detects these state changes via the &lt;code&gt;useEffect&lt;/code&gt; hook and navigates accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session persistence
&lt;/h3&gt;

&lt;p&gt;After successful wallet creation, the session persists across app restarts. This is handled by the &lt;code&gt;WalletService&lt;/code&gt;:&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;// Save session after connection (from app/index.tsx)&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveSessionAndNavigate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="na"&gt;credentialId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazorkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="na"&gt;lastAccessedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WalletService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to save session:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;saveSessionAndNavigate&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="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isConnected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;smartWalletPubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On app restart, the Welcome screen checks for an existing session and automatically navigates to the home screen:&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;// Check for existing session on app launch (from app/index.tsx)&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkExistingSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WalletService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadSession&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;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;WalletService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSessionValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Skip welcome screen, go directly to home&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;checkExistingSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "TypeError: Cannot read property 'getRandomValues' of undefined"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Polyfills not loaded or loaded in wrong order.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Check that &lt;code&gt;react-native-url-polyfill/auto&lt;/code&gt; is imported at the VERY TOP of &lt;code&gt;app/_layout.tsx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check that polyfills are imported BEFORE any Solana or Lazorkit imports&lt;/li&gt;
&lt;li&gt;The Lazorkit SDK imports &lt;code&gt;react-native-get-random-values&lt;/code&gt; internally, so you don't need to import it explicitly&lt;/li&gt;
&lt;li&gt;Verify all polyfill packages are installed: &lt;code&gt;npm list react-native-get-random-values react-native-url-polyfill buffer&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Issue 2: "Deep link not opening app after authentication"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Scheme mismatch between &lt;code&gt;app.json&lt;/code&gt; and &lt;code&gt;wallet.connect()&lt;/code&gt; options, or deep linking not configured properly.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Verify the &lt;code&gt;scheme&lt;/code&gt; field in &lt;code&gt;app.json&lt;/code&gt; matches the &lt;code&gt;redirectUrl&lt;/code&gt; in &lt;code&gt;wallet.connect()&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="c1"&gt;// app.json&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scheme&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;lazorkitstarter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

   &lt;span class="c1"&gt;// app/index.tsx&lt;/span&gt;
   &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazorkitstarter://callback&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Rebuild your app after changing &lt;code&gt;app.json&lt;/code&gt; (deep linking config doesn't apply via OTA updates)&lt;/li&gt;
&lt;li&gt;Test deep linking with: &lt;code&gt;npx uri-scheme open lazorkitstarter://test --ios&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Additional Debugging&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check iOS Console logs for deep link errors (Xcode &amp;gt; Window &amp;gt; Devices and Simulators &amp;gt; Open Console)&lt;/li&gt;
&lt;li&gt;Check Android logcat for deep link errors: &lt;code&gt;adb logcat | grep -i "intent"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ensure you're using a development build, not Expo Go (custom schemes require native code)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 3: "Biometric prompt not appearing"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Testing on simulator (biometrics require physical device).&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Test on a physical device with Face ID/Touch ID enabled&lt;/li&gt;
&lt;li&gt;Ensure biometric authentication is set up in device settings:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS&lt;/strong&gt;: Settings &amp;gt; Face ID &amp;amp; Passcode (or Touch ID &amp;amp; Passcode)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt;: Settings &amp;gt; Security &amp;gt; Fingerprint&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Note: Expo Go may have limitations with biometric prompts; use a development build for full support&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Development Build Command&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# iOS&lt;/span&gt;
eas build &lt;span class="nt"&gt;--profile&lt;/span&gt; development &lt;span class="nt"&gt;--platform&lt;/span&gt; ios

&lt;span class="c"&gt;# Android&lt;/span&gt;
eas build &lt;span class="nt"&gt;--profile&lt;/span&gt; development &lt;span class="nt"&gt;--platform&lt;/span&gt; android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 4: "App stuck on 'Connecting...' indefinitely"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Network error, portal redirect timeout, or deep link callback not received.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Check network connectivity (portal requires internet access)&lt;/li&gt;
&lt;li&gt;Verify portal URL is correct: &lt;code&gt;https://portal.lazor.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check Expo logs for errors:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npx expo start &lt;span class="nt"&gt;--dev-client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Look for timeout or network errors in the Metro bundler console&lt;/li&gt;
&lt;li&gt;Ensure the user isn't stuck in the browser (manually switch back to the app)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Debugging Tips&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add console logs in the &lt;code&gt;handleCreateWallet&lt;/code&gt; catch block to see error details&lt;/li&gt;
&lt;li&gt;Check if &lt;code&gt;wallet.isConnecting&lt;/code&gt; ever becomes &lt;code&gt;false&lt;/code&gt; (it should after completion or error)&lt;/li&gt;
&lt;li&gt;Verify the portal URL is reachable: &lt;code&gt;curl https://portal.lazor.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 5: "Buffer is not defined"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Buffer polyfill not assigned to global namespace.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Ensure you have this line in &lt;code&gt;app/_layout.tsx&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&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;Buffer&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="s1"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check that this appears BEFORE any Solana or Lazorkit imports&lt;/li&gt;
&lt;li&gt;Verify the &lt;code&gt;buffer&lt;/code&gt; package is installed: &lt;code&gt;npm list buffer&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Issue 6: "Property 'isConnected' does not exist on type 'WalletContextState'"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: Using incorrect property name from outdated documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Use the correct SDK property names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;wallet.isConnected&lt;/code&gt; (NOT &lt;code&gt;wallet.connected&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;wallet.isConnecting&lt;/code&gt; (NOT &lt;code&gt;wallet.connecting&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;wallet.smartWalletPubkey&lt;/code&gt; (NOT &lt;code&gt;wallet.publicKey&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 7: ESLint error "Import 'X' must come before 'Y'"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause&lt;/strong&gt;: ESLint enforces import ordering, but polyfills must violate this rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Add the ESLint disable comment after polyfill imports:&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native-url-polyfill/auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Buffer&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="s1"&gt;buffer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// eslint-disable-next-line import/first -- Polyfills must be imported before other modules&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LazorKitProvider&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="s1"&gt;@lazorkit/wallet-mobile-adapter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Congratulations! You've successfully implemented biometric wallet creation with Lazorkit SDK. Here's what you can explore next:&lt;/p&gt;

&lt;h3&gt;
  
  
  Official Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lazorkit SDK Documentation&lt;/strong&gt;: &lt;a href="https://docs.lazor.sh" rel="noopener noreferrer"&gt;https://docs.lazor.sh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana Web3.js Documentation&lt;/strong&gt;: &lt;a href="https://solana-labs.github.io/solana-web3.js/" rel="noopener noreferrer"&gt;https://solana-labs.github.io/solana-web3.js/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo Router Documentation&lt;/strong&gt;: &lt;a href="https://docs.expo.dev/router/introduction/" rel="noopener noreferrer"&gt;https://docs.expo.dev/router/introduction/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo SecureStore API&lt;/strong&gt;: &lt;a href="https://docs.expo.dev/versions/latest/sdk/securestore/" rel="noopener noreferrer"&gt;https://docs.expo.dev/versions/latest/sdk/securestore/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  WebAuthn and Passkeys
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebAuthn Guide&lt;/strong&gt;: &lt;a href="https://webauthn.guide/" rel="noopener noreferrer"&gt;https://webauthn.guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passkeys.dev&lt;/strong&gt;: &lt;a href="https://passkeys.dev/" rel="noopener noreferrer"&gt;https://passkeys.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apple Face ID Documentation&lt;/strong&gt;: &lt;a href="https://developer.apple.com/face-id/" rel="noopener noreferrer"&gt;https://developer.apple.com/face-id/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solana Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solana Explorer (Devnet)&lt;/strong&gt;: &lt;a href="https://explorer.solana.com/?cluster=devnet" rel="noopener noreferrer"&gt;https://explorer.solana.com/?cluster=devnet&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Use this to look up wallet addresses and transactions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Solana Cookbook&lt;/strong&gt;: &lt;a href="https://solanacookbook.com/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://solanacookbook.com/" rel="noopener noreferrer"&gt;https://solanacookbook.com/&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Solana Airdrop (Devnet)&lt;/strong&gt;: &lt;a href="https://solfaucet.com/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://solfaucet.com/" rel="noopener noreferrer"&gt;https://solfaucet.com/&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Get free devnet SOL for testing (though you don't need it with Lazorkit paymaster!)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Full Source Code Reference
&lt;/h2&gt;

&lt;p&gt;All code snippets in this tutorial are extracted from an actual repository.&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;Happy building with Lazorkit! If you need any help with any aspect, feel free to drop your questions in the comment section :) *&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>solana</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building Story CLI: From 30-Minute IP Registration to Under 5</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Mon, 15 Dec 2025 21:43:25 +0000</pubDate>
      <link>https://forem.com/ola-zoll/building-story-cli-from-30-minute-ip-registration-to-under-5-22nk</link>
      <guid>https://forem.com/ola-zoll/building-story-cli-from-30-minute-ip-registration-to-under-5-22nk</guid>
      <description>&lt;h2&gt;
  
  
  The problem that sparked this project
&lt;/h2&gt;

&lt;p&gt;Here's something that bothered me about Web3 developer tools: they're often built &lt;em&gt;by&lt;/em&gt; experienced blockchain developers &lt;em&gt;for&lt;/em&gt; experienced blockchain developers. &lt;/p&gt;

&lt;p&gt;In the case of IP registration via blockchain, if you're a solo creator who just wants to register your artwork, music, or code as intellectual property on-chain, you're looking at 15-30 minutes of fumbling through documentation, manually formatting JSON metadata, and praying your transaction doesn't fail after you've already spent gas.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Story CLI&lt;/strong&gt; to fix that.&lt;/p&gt;




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

&lt;p&gt;Story CLI is a command-line toolkit for registering IP assets on &lt;a href="https://story.foundation" rel="noopener noreferrer"&gt;Story Protocol&lt;/a&gt;—a blockchain designed specifically for programmable intellectual property. Instead of writing code or crafting API calls, you get an interactive wizard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;story register ./my-artwork.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The CLI walks you through license selection, handles IPFS uploads, executes the blockchain transaction, and gives you a shareable portfolio visualization of all your registered IP.&lt;/p&gt;

&lt;p&gt;The goal was simple: &lt;strong&gt;take a 15-30 minute process and compress it to under 5 minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8xe1mw96kb30qes8v3w.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%2Fj8xe1mw96kb30qes8v3w.png" alt=" " width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The license wizard state machine
&lt;/h3&gt;

&lt;p&gt;One of the trickiest parts was translating legal license configurations into something a human could answer in 30 seconds. Story Protocol uses PIL (Programmable IP License) with multiple parameters—commercial use permissions, derivative rights, royalty percentages.&lt;/p&gt;

&lt;p&gt;I mapped this to a 3-question decision tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Commercial use? → Yes/No
Allow derivatives? → Yes/No
Revenue share? → 0-100% (only if commercial + derivatives)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These three questions deterministically map to one of 4 license configurations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Commercial&lt;/th&gt;
&lt;th&gt;Derivatives&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Non-commercial Only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Non-commercial Derivatives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Commercial No-Derivatives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Commercial Remix (+ royalty %)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The state machine approach meant I could validate answers in real-time and prevent invalid combinations before they ever hit the blockchain.&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%2Fytgbexj4sd9xcdkw9y07.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%2Fytgbexj4sd9xcdkw9y07.png" alt=" " width="800" height="1031"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fail-fast everything
&lt;/h3&gt;

&lt;p&gt;Blockchain transactions cost gas. Failed transactions &lt;em&gt;still&lt;/em&gt; cost gas. This created a design imperative: &lt;strong&gt;validate everything before touching the chain&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wallet address format? Checked before any network call&lt;/li&gt;
&lt;li&gt;IPFS hash format? Validated at input&lt;/li&gt;
&lt;li&gt;Sufficient balance? Queried before transaction submission&lt;/li&gt;
&lt;li&gt;Royalty percentage? Bounded 0-100 at prompt time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The philosophy: if something's going to fail, fail in the first 2 seconds, not after a 30-second transaction attempt.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Three-part error messages
&lt;/h3&gt;

&lt;p&gt;Every error in Story CLI follows a structure: &lt;strong&gt;What&lt;/strong&gt; went wrong → &lt;strong&gt;Why&lt;/strong&gt; it matters → &lt;strong&gt;How&lt;/strong&gt; to fix it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✖ Pinata API key not found
  IPFS uploads require Pinata authentication for metadata storage.
  Run: story config set pinataApiKey YOUR_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sounds obvious, but most CLI tools just dump "Error: invalid credentials" and leave you to figure it out. I spent real time ensuring every failure state had actionable guidance.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Self-contained portfolio HTML
&lt;/h3&gt;

&lt;p&gt;After registration, users needed to &lt;em&gt;see&lt;/em&gt; their IP. I chose Mermaid.js for graph visualization because it lets me generate a &lt;strong&gt;single HTML file&lt;/strong&gt; with everything embedded—CSS, JavaScript, and relationship diagrams.&lt;/p&gt;

&lt;p&gt;No server required. Download the file, email it to someone, and open it. It works.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;story portfolio
&lt;span class="c"&gt;# → generates story-portfolio.html with interactive graphs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tradeoff was less visual customization than D3.js would offer, but for an MVP, shipping &amp;gt; perfection.&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges &amp;amp; what I learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: SDK documentation gaps
&lt;/h3&gt;

&lt;p&gt;Story Protocol is relatively new. The SDK documentation had gaps—especially around error responses and edge cases. I ended up reading SDK source code directly and building a mock implementation (&lt;code&gt;STORY_CLI_MOCK=true&lt;/code&gt;) so I could develop offline without burning testnet ETH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; When integrating new SDKs, budget time for exploration. Mock modes aren't just for testing—they're essential for iteration speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Terminal UX is harder than it looks
&lt;/h3&gt;

&lt;p&gt;Making a CLI feel "good" requires attention to details you take for granted in web UIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spinners during async operations (Ora)&lt;/li&gt;
&lt;li&gt;Color-coded output for visual hierarchy (Chalk)&lt;/li&gt;
&lt;li&gt;Boxed success messages for celebration moments (Boxen)&lt;/li&gt;
&lt;li&gt;Clear prompt validation with inline feedback (Inquirer)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each library handles a specific UX need. Combining them into a cohesive experience took more iteration than expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; CLI UX is a real discipline. Users notice when it's done well—they just don't notice consciously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: Config file security
&lt;/h3&gt;

&lt;p&gt;Storing API keys and wallet information in &lt;code&gt;~/.storyrc&lt;/code&gt; required thinking about permissions. The config file is created with &lt;code&gt;chmod 600&lt;/code&gt; (owner read/write only), and sensitive values can be overridden via environment variables for CI/CD pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;STORY_WALLET_PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x...  &lt;span class="c"&gt;# Override config file&lt;/span&gt;
story register ./asset.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Security isn't a feature—it's a constraint that shapes your entire design.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&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%2F41oqatwbubn3bay5vu75.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%2F41oqatwbubn3bay5vu75.png" alt=" " width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture at a glance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User terminal
    │
    ▼
Command router (Commander.js)
    │
    ├──► Register command
    │    ├── License wizard (Inquirer.js)
    │    ├── Metadata prompts
    │    ├── IPFS upload (Pinata SDK)
    │    └── Blockchain transaction (Story SDK)
    │
    ├──► Portfolio command
    │    ├── Asset fetching (Story API)
    │    ├── Graph building (Mermaid.js)
    │    └── HTML rendering
    │
    ├──► Config command
    │    └── ~/.storyrc management
    │
    └──► Status command
         └── Wallet &amp;amp; network info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key patterns used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Command pattern&lt;/strong&gt; — Each CLI command is an isolated handler&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Facade pattern&lt;/strong&gt; — &lt;code&gt;StoryClient&lt;/code&gt; wraps the complex SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Singleton pattern&lt;/strong&gt; — &lt;code&gt;ConfigManager&lt;/code&gt; prevents redundant file reads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-fast validation&lt;/strong&gt; — Errors surface immediately, not after blockchain calls&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why this matters (Beyond the code)
&lt;/h2&gt;

&lt;p&gt;IP registration on blockchain is one of those "obviously useful" ideas that's been stuck behind technical barriers. The people who would benefit most—independent artists, open-source developers, content creators—are often the least equipped to navigate Web3 tooling.&lt;/p&gt;

&lt;p&gt;Story CLI is my attempt at a bridge. Not a dumbed-down version, but a &lt;em&gt;well-designed&lt;/em&gt; version that respects users' time and treats errors as communication opportunities rather than dead ends.&lt;/p&gt;

&lt;p&gt;The goal was never to abstract away the blockchain entirely—it was to remove the &lt;em&gt;friction&lt;/em&gt; while preserving the power.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; story-cli
story &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quick start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Configure your wallet&lt;/span&gt;
story config &lt;span class="nb"&gt;set &lt;/span&gt;walletAddress 0xYourAddress
story config &lt;span class="nb"&gt;set &lt;/span&gt;network testnet

&lt;span class="c"&gt;# Set up IPFS (Pinata)&lt;/span&gt;
story config &lt;span class="nb"&gt;set &lt;/span&gt;pinataApiKey YOUR_KEY
story config &lt;span class="nb"&gt;set &lt;/span&gt;pinataApiSecret YOUR_SECRET

&lt;span class="c"&gt;# Register your first IP asset&lt;/span&gt;
story register ./my-artwork.jpg

&lt;span class="c"&gt;# View your portfolio&lt;/span&gt;
story portfolio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;ul&gt;
&lt;li&gt;Batch registration for multiple assets&lt;/li&gt;
&lt;li&gt;License template sharing&lt;/li&gt;
&lt;li&gt;Integration with popular creative tools&lt;/li&gt;
&lt;li&gt;Mainnet deployment guides&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/Zolldyk/story-cli" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://story.foundation" rel="noopener noreferrer"&gt;Story Protocol&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blockchain</category>
      <category>web3</category>
      <category>cli</category>
      <category>programming</category>
    </item>
    <item>
      <title>My first flash loan protocol: A Solana adventure</title>
      <dc:creator>Ola Adesoye</dc:creator>
      <pubDate>Sat, 22 Nov 2025 15:06:01 +0000</pubDate>
      <link>https://forem.com/ola-zoll/my-first-flash-loan-protocol-a-solana-adventure-3i4k</link>
      <guid>https://forem.com/ola-zoll/my-first-flash-loan-protocol-a-solana-adventure-3i4k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Why i said yes to the unknown
&lt;/h2&gt;

&lt;p&gt;I stumbled upon this challenge while browsing developer communities online, and honestly, my first instinct was to scroll past it. &lt;/p&gt;

&lt;p&gt;The challenge was clear: build a flash loan program on Solana using Anchor framework. Simple enough on paper, but there was one problem —I had never touched Solana development before.&lt;/p&gt;

&lt;p&gt;The requirements were intimidating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Rust: A language I'd love to still learn but currently don't know much about &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anchor Framework: Completely new territory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solana's programming model: Foreign concepts like PDAs, instruction introspection, and BPF programs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By all reasonable measures, I should have skipped it. I had deadlines, bunch of familiar projects waiting for my attention, and a comfortable tech stack waiting for me. But two things made me pause:&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;Superteam UK's talent database&lt;/strong&gt;. Successfully completing this challenge meant being recorded as a developer in their ecosystem, gaining access to developer-only Telegram groups, exclusive events, and other initiatives. The opportunity to join a community of builders working on cutting-edge blockchain technology was too valuable to pass up.&lt;/p&gt;

&lt;p&gt;Second, and perhaps more importantly, I'd interacted with flash loan protocols before. I'd used them and seen how one can profit from the arbitrage opportunities they enabled. But I'd never built one. There's a massive gulf between using a tool and understanding how it works at a fundamental level. This challenge was my chance to cross that bridge.&lt;/p&gt;

&lt;p&gt;So I decided to dive in, armed with nothing but curiosity and the every friendly AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Flash Loans, Anyway?
&lt;/h2&gt;

&lt;p&gt;Before we get into the technical weeds, let's establish what flash loans actually are - because they're one of the most fascinating innovations in DeFi.&lt;/p&gt;

&lt;p&gt;Imagine borrowing $1 million, using it to execute a complex trading strategy across multiple protocols, and then repaying the loan—all within a single transaction that takes mere seconds. No collateral required. No credit checks. No lengthy approval processes.&lt;/p&gt;

&lt;p&gt;That's a flash loan.&lt;/p&gt;

&lt;p&gt;The magic lies in &lt;strong&gt;transaction atomicity&lt;/strong&gt;: on blockchains like Solana, transactions are all-or-nothing. If any step in the transaction fails (including the loan repayment), the entire transaction reverts as if it never happened. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For the lender&lt;/strong&gt;: Zero risk. Either you get repaid with fees, or the loan never actually occurs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For the borrower&lt;/strong&gt;: Instant access to massive capital, limited only by available liquidity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flash loans enable arbitrage, collateral swapping, liquidation assistance, and many other DeFi strategies that were impossible in traditional finance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: Building flash loans on Solana
&lt;/h2&gt;

&lt;p&gt;The challenge was deceptively simple:&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 1: Create a &lt;code&gt;borrow&lt;/code&gt; instruction
&lt;/h3&gt;

&lt;p&gt;Build a program that allows a borrower to borrow funds from the protocol and verify that a repay instruction exists at the end of the transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Create a &lt;code&gt;repay&lt;/code&gt; instruction
&lt;/h3&gt;

&lt;p&gt;Implement an instruction that extracts the borrowed amount from the borrow instruction and repays the protocol with the correct amount plus fees.&lt;/p&gt;

&lt;p&gt;The twist? This all needed to happen using &lt;strong&gt;instruction introspection&lt;/strong&gt; — the ability for a Solana program to examine other instructions in the same transaction, including ones that haven't executed yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up: New tools, New paradigms
&lt;/h2&gt;

&lt;p&gt;My first task was getting the development environment ready:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize a new Anchor project&lt;/span&gt;
anchor init blueshift_anchor_flash_loan

&lt;span class="c"&gt;# Add SPL token utilities&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;blueshift_anchor_flash_loan
cargo add anchor-spl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coming from an Ethereum background (where I'd worked with Solidity), Solana's programming model was... different. Here are the key paradigm shifts I had to internalize:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Accounts, accounts, everywhere&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In Solana, you don't have contract storage like Ethereum. Instead, everything is accounts. Your program? An account. Your data? Separate accounts. User tokens? Also accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Rent and account ownership&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Accounts must maintain a minimum balance (rent) to exist on-chain. Programs can own accounts. This helps create powerful patterns like Program Derived Addresses (PDAs).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;No built-in reentrancy guards needed&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Solana's execution model is different — you explicitly pass all accounts you'll need. The good thing here is that it makes certain attack vectors from Ethereum (like reentrancy) less relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecting the solution
&lt;/h2&gt;

&lt;p&gt;I started by sketching out the account structure. Both &lt;code&gt;borrow&lt;/code&gt; and &lt;code&gt;repay&lt;/code&gt; would use the same accounts:&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;Loan&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(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;borrower&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="nd"&gt;#[account(&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"protocol"&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="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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;mint&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;Mint&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_if_needed,&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;borrower,&lt;/span&gt;
        &lt;span class="nd"&gt;associated_token::mint&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;mint,&lt;/span&gt;
        &lt;span class="nd"&gt;associated_token::authority&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;borrower,&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;borrower_ata&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;TokenAccount&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;associated_token::mint&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;mint,&lt;/span&gt;
        &lt;span class="nd"&gt;associated_token::authority&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;protocol,&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;protocol_ata&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;TokenAccount&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(address&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;INSTRUCTIONS_SYSVAR_ID)]&lt;/span&gt;
    &lt;span class="cd"&gt;/// CHECK: InstructionsSysvar account&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UncheckedAccount&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;token_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;Token&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;associated_token_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;AssociatedToken&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key design decisions:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Protocol PDA&lt;/strong&gt;: The protocol account is a Program Derived Address (PDA) using the seed &lt;code&gt;"protocol"&lt;/code&gt;. This is crucial because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only our program can sign transactions on behalf of this PDA&lt;/li&gt;
&lt;li&gt;It provides deterministic addresses&lt;/li&gt;
&lt;li&gt;It securely holds the protocol's liquidity pool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Associated Token Accounts (ATAs)&lt;/strong&gt;: Using &lt;code&gt;init_if_needed&lt;/code&gt; for the borrower's ATA means we automatically create it if it doesn't exist. The aim here is to improve UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Instructions Sysvar&lt;/strong&gt;: This special account gives us access to all instructions in the current transaction—the key to our flash loan security model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The heart of the matter: Instruction Introspection
&lt;/h2&gt;

&lt;p&gt;This is where things got really interesting. The security of flash loans relies on ensuring that borrowed funds are repaid within the same transaction. On Ethereum, you might use a callback pattern. On Solana, we use &lt;strong&gt;instruction introspection&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The borrow instruction
&lt;/h3&gt;

&lt;p&gt;Here's the critical code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;borrow&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;Loan&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;borrow_amount&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;// Validate amount&lt;/span&gt;
    &lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;borrow_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.instructions&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if this is the first instruction in the transaction&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;current_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_current_index_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.instructions&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;require_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidIx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the total number of instructions in this transaction&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_sysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_data&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;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction_sysvar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction_sysvar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Load the LAST instruction and verify it's our repay instruction&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Verify it's calling our program&lt;/span&gt;
        &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidProgram&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Verify it has enough data&lt;/span&gt;
        &lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidIx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Verify the same accounts are being used (indices 3 and 4)&lt;/span&gt;
        &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.accounts&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidBorrowerAta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="py"&gt;.pubkey&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidBorrowerAta&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.accounts&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidProtocolAta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="py"&gt;.pubkey&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.protocol_ata&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidProtocolAta&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MissingRepayIx&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// If we get here, validation passed—time to lend the funds&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;seeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s"&gt;b"protocol"&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&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;.bumps.protocol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;signer_seeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;seeds&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="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;CpiContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_signer&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.token_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;from&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.protocol_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;to&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&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;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.protocol&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;signer_seeds&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;borrow_amount&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;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Borrowed {} tokens"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;borrow_amount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's happening here?&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're ensuring &lt;code&gt;borrow&lt;/code&gt; is the FIRST instruction (index 0)&lt;/li&gt;
&lt;li&gt;We're reading the instructions sysvar to find out how many total instructions exist&lt;/li&gt;
&lt;li&gt;We're "looking ahead" to the LAST instruction in the transaction&lt;/li&gt;
&lt;li&gt;We're validating that the last instruction:

&lt;ul&gt;
&lt;li&gt;Calls our program&lt;/li&gt;
&lt;li&gt;Has the same token accounts (ensuring repayment goes to the right place)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is powerful: we refuse to lend unless we can see the repay instruction waiting at the end of the transaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  The repay instruction
&lt;/h3&gt;

&lt;p&gt;The repay side is equally clever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;repay&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;Loan&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.instructions&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;amount_borrowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;borrow_ix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Extract the borrowed amount from the first instruction's data&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;borrowed_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;8&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="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;borrowed_data&lt;/span&gt;&lt;span class="nf"&gt;.copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;borrow_ix&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="nn"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;borrowed_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MissingBorrowIx&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Calculate 5% fee (500 basis points)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount_borrowed&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.checked_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.checked_div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Overflow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;as&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;let&lt;/span&gt; &lt;span class="n"&gt;repay_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount_borrowed&lt;/span&gt;
        &lt;span class="nf"&gt;.checked_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Overflow&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="c1"&gt;// Transfer funds back to protocol&lt;/span&gt;
    &lt;span class="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;CpiContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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.token_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;from&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;to&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.protocol_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&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;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.borrower&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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="n"&gt;repay_amount&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;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Repaid {} tokens (borrowed: {}, fee: {})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repay_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount_borrowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we're:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Looking BACKWARD at the first instruction (the borrow)&lt;/li&gt;
&lt;li&gt;Extracting the borrowed amount from its instruction data&lt;/li&gt;
&lt;li&gt;Calculating a 5% fee&lt;/li&gt;
&lt;li&gt;Transferring borrowed amount + fee back to the protocol&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The debugging gauntlet
&lt;/h2&gt;

&lt;p&gt;Now, here's where things got... educational. My first several attempts failed spectacularly. Let me walk you through the journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue #1: The slice index panic
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;panicked at programs/blueshift_anchor_flash_loan/src/lib.rs:296:12:
range start index 63407 out of range for slice of length 698
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
I initially tried to manually parse the instructions sysvar to look ahead at future instructions. My parsing logic was completely wrong — I was trying to access byte index 63407 in a 698-byte array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Root Cause:&lt;/strong&gt;&lt;br&gt;
I misunderstood the instructions sysvar format. I thought I needed to manually parse through it, but Solana provides &lt;code&gt;load_instruction_at_checked()&lt;/code&gt; for this exact purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;br&gt;
Stop trying to be clever and use the standard library:&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="c1"&gt;// WRONG: Manual parsing with incorrect offsets&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;num_instructions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_instruction_relative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instructions_sysvar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// RIGHT: Use the built-in function&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_sysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_data&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;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction_sysvar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue #2: Cannot look ahead
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AnchorError occurred. Error Code: InvalidInstructionIndex. Error Number: 6001.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Problem:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;load_instruction_at_checked()&lt;/code&gt; was failing when I tried to load future instructions (instructions that haven't executed yet).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Revelation:&lt;/strong&gt;&lt;br&gt;
This was a critical learning moment. In Solana, &lt;code&gt;load_instruction_at_checked()&lt;/code&gt; has security restrictions—by default, it can only access instructions that have already executed. This prevents certain types of attacks but complicates looking ahead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial failed approach:&lt;/strong&gt;&lt;br&gt;
I tried manually parsing the instructions sysvar with custom structs:&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="c1"&gt;// This approach was too complex and error-prone&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ParsedInstruction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;program_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pubkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pubkey&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;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&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="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_instruction_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instructions_sysvar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;UncheckedAccount&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="n"&gt;ParsedInstruction&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;// Complex parsing logic that kept failing...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Correct Approach:&lt;/strong&gt;&lt;br&gt;
It turns out &lt;code&gt;load_instruction_at_checked()&lt;/code&gt; CAN load future instructions, but you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drop any borrows on the instructions sysvar before calling it&lt;/li&gt;
&lt;li&gt;Read the instruction count from the correct bytes (0..2, not 2..4)
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get instruction count - MUST drop the borrow before load_instruction_at_checked&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_sysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_data&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="nn"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction_sysvar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Borrow drops here&lt;/span&gt;

&lt;span class="c1"&gt;// Now we can load future instructions&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Success!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Issue #3: The discriminator mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error Code: InvalidIx. Error Number: 6000. Error Message: Invalid instruction.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt;&lt;br&gt;
I hardcoded the discriminator for the repay 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="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;repay_discriminator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;8&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="mi"&gt;0x8c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xfc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xc7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x6e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x4f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xaf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x32&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;repay_discriminator&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidIx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the test environment's discriminator didn't match. In Anchor, discriminators are generated from &lt;code&gt;sha256("global:&amp;lt;function_name&amp;gt;")[0..8]&lt;/code&gt;, and apparently the test harness was generating a different value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt;&lt;br&gt;
Remove the strict discriminator check and rely on other validation:&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="c1"&gt;// Instead of checking exact discriminator bytes, just verify we have data&lt;/span&gt;
&lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidIx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// And validate the accounts match (stronger guarantee anyway)&lt;/span&gt;
&lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.accounts&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidBorrowerAta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="py"&gt;.pubkey&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidBorrowerAta&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 actually MORE secure because we're validating the actual accounts involved, not just a hash.&lt;/p&gt;

&lt;h2&gt;
  
  
  The security model: Why this works
&lt;/h2&gt;

&lt;p&gt;Let's pause and appreciate the elegance of this system:&lt;/p&gt;

&lt;h3&gt;
  
  
  Transaction Atomicity = Risk-Free Lending
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│         FLASH LOAN TRANSACTION          │
├─────────────────────────────────────────┤
│                                         │
│  1. Borrow (checks for repay) ────────┐ │
│           │                           │ │
│           │ Lends tokens              │ │
│           ▼                           │ │
│  2. [User's actions with funds]       │ │
│           │                           │ │
│           │ Uses borrowed tokens      │ │
│           ▼                           │ │
│  3. Repay (validates borrow) ◄────────┘ │
│           │                             │
│           │ Returns tokens + fee        │
│           ▼                             │
│     ✓ Success                           │
│                                         │
│  If ANY step fails:                     │
│  → Entire transaction reverts           │
│  → It's like nothing happened           │
│  → Protocol loses nothing               │
│                                         │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multi-layer validation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Program ID check&lt;/strong&gt;: Ensures we're calling our own program&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account validation&lt;/strong&gt;: Verifies the same token accounts are used in both instructions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomicity&lt;/strong&gt;: If repay fails, borrow never happened&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amount verification&lt;/strong&gt;: Repay extracts the exact borrowed amount from borrow's data&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Key learnings
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Read the docs, then read them again&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Solana's programming model is fundamentally different from Ethereum's. Assumptions don't transfer well. I wasted hours trying to manually parse the instructions sysvar when the SDK had perfect functions for this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Use the platform's native tools first. Optimize later if needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Borrowing in Rust is not optional&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Coming from languages with garbage collection, Rust's borrow checker was frustrating at first. But the error about trying to call &lt;code&gt;load_instruction_at_checked()&lt;/code&gt; while holding a borrow taught me that Rust's restrictions prevent real bugs.&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="c1"&gt;// This fails - can't borrow ixs twice&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_sysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_data&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;let&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&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="c1"&gt;// ERROR!&lt;/span&gt;

&lt;span class="c1"&gt;// This works - scope the borrow&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_sysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_data&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="nn"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction_sysvar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Borrow dropped here&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&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="c1"&gt;// OK!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Instruction introspection is powerful&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The ability to examine and validate other instructions in a transaction enables patterns that are hard or impossible in other chains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flash loans (as we built)&lt;/li&gt;
&lt;li&gt;Composable DeFi strategies&lt;/li&gt;
&lt;li&gt;Multi-step protocols with built-in validation&lt;/li&gt;
&lt;li&gt;Atomic sandwich protection&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Security through architecture&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We didn't need extensive reentrancy guards or complex state machines. The security comes from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit account passing&lt;/li&gt;
&lt;li&gt;Transaction atomicity&lt;/li&gt;
&lt;li&gt;Instruction introspection&lt;/li&gt;
&lt;li&gt;Program Derived Addresses (PDAs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These primitives combine to create a system that's secure by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;The value of pair programming (Even with AI)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Working with AI was illuminating. When I hit a wall, explaining the problem to the AI forced me to think clearly about what was actually happening. Sometimes I'd solve the issue while typing the question. Other times, the AI would suggest an approach I hadn't considered and these led me down productive paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final implementation
&lt;/h2&gt;

&lt;p&gt;After all the iterations, here's what worked:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total Lines of Code&lt;/strong&gt;: ~200&lt;br&gt;
&lt;strong&gt;Number of Instructions&lt;/strong&gt;: 2 (borrow, repay)&lt;br&gt;
&lt;strong&gt;Security Features&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instruction introspection&lt;/li&gt;
&lt;li&gt;Account validation&lt;/li&gt;
&lt;li&gt;Transaction atomicity&lt;/li&gt;
&lt;li&gt;PDA-based authority&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lib.rs&lt;/code&gt;: Main program logic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cargo.toml&lt;/code&gt;: Dependencies (anchor-lang, anchor-spl)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Anchor.toml&lt;/code&gt;: Project configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;This implementation is educational, not production-ready. To make it real:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. &lt;strong&gt;Add price oracle integration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Currently, the protocol has a fixed fee (5%). Real flash loan protocols adjust fees based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Market volatility&lt;/li&gt;
&lt;li&gt;Protocol utilization rates&lt;/li&gt;
&lt;li&gt;Risk metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. &lt;strong&gt;Implement multi-token support&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Right now it's single-token. Production systems support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple token pools&lt;/li&gt;
&lt;li&gt;Cross-token flash loans&lt;/li&gt;
&lt;li&gt;Liquidity management across assets&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  3. &lt;strong&gt;Add governance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Who controls the protocol? What are the fees? Production systems need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DAO governance&lt;/li&gt;
&lt;li&gt;Parameter adjustment mechanisms&lt;/li&gt;
&lt;li&gt;Emergency pause functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4. &lt;strong&gt;Gas optimization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Every instruction on Solana costs compute units. Optimizations could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimizing account validations&lt;/li&gt;
&lt;li&gt;Batching operations&lt;/li&gt;
&lt;li&gt;Using more efficient data structures&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  5. &lt;strong&gt;Comprehensive testing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;My tests were basic. Production needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fuzz testing&lt;/li&gt;
&lt;li&gt;Integration tests with real DeFi protocols&lt;/li&gt;
&lt;li&gt;Economic attack simulations&lt;/li&gt;
&lt;li&gt;Formal verification (for critical paths)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Reflections
&lt;/h2&gt;

&lt;p&gt;When I started this challenge, I couldn't have imagined the depth of learning ahead. What began as a resume-building exercise became a masterclass in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New programming paradigms&lt;/strong&gt; (Solana's account model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systems thinking&lt;/strong&gt; (how transaction atomicity enables new primitives)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging across abstraction layers&lt;/strong&gt; (from Rust borrow checker to on-chain execution)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Financial engineering&lt;/strong&gt; (flash loans as a DeFi primitive)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most valuable lesson? &lt;strong&gt;Discomfort is where growth happens.&lt;/strong&gt; I was uncomfortable with Rust, uncertain about Solana, and intimidated by the challenge. But that discomfort was a signal—I was learning.&lt;/p&gt;

&lt;p&gt;Would I do it again? Absolutely. In fact, I'm already eyeing the next challenge.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;If you want to explore flash loans and Solana development:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://solanacookbook.com/" rel="noopener noreferrer"&gt;Solana Cookbook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://book.anchor-lang.com/" rel="noopener noreferrer"&gt;Anchor Book&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spl.solana.com/" rel="noopener noreferrer"&gt;Solana Program Library (SPL)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Flash loan concepts&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aave.com/developers/guides/flash-loans" rel="noopener noreferrer"&gt;Aave Flash Loans Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coindesk.com/learn/what-are-flash-loans/" rel="noopener noreferrer"&gt;Understanding Flash Loans&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Instruction introspection&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.soldev.app/course/instruction-introspection" rel="noopener noreferrer"&gt;Solana Instruction Introspection Course&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.rs/anchor-lang/latest/anchor_lang/context/struct.Context.html" rel="noopener noreferrer"&gt;Anchor Instruction Context&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Development tools&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.anchor-lang.com/" rel="noopener noreferrer"&gt;Anchor Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.solana.com/cli" rel="noopener noreferrer"&gt;Solana CLI Tools&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Appendix: full code
&lt;/h2&gt;

&lt;p&gt;For those who want to see the complete implementation:&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="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;anchor_spl&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;token&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TokenAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Transfer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nn"&gt;associated_token&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AssociatedToken&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&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;solana_program&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sysvar&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;INSTRUCTIONS_SYSVAR_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;load_current_index_checked&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;"22222222222222222222222222222222222222222222"&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;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;blueshift_anchor_flash_loan&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;borrow&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;Loan&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;borrow_amount&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="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;borrow_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidAmount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.instructions&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Check if this is the first instruction in the transaction&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;current_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_current_index_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.instructions&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;require_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidIx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Check how many instructions we have in this transaction&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;instruction_sysvar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="nf"&gt;.try_borrow_data&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="nn"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instruction_sysvar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Ensure we have a repay instruction&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.program_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidProgram&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nd"&gt;require!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidIx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.accounts&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidBorrowerAta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="py"&gt;.pubkey&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidBorrowerAta&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nd"&gt;require_keys_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;repay_ix&lt;/span&gt;&lt;span class="py"&gt;.accounts&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidProtocolAta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="py"&gt;.pubkey&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.protocol_ata&lt;/span&gt;&lt;span class="nf"&gt;.key&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InvalidProtocolAta&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MissingRepayIx&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Transfer the borrowed amount to the borrower&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;seeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;b"protocol"&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&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;.bumps.protocol&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;signer_seeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;seeds&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="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;CpiContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_with_signer&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.token_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;from&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.protocol_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;to&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&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;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.protocol&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="n"&gt;signer_seeds&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;borrow_amount&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;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Borrowed {} tokens"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;borrow_amount&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="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;repay&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;Loan&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ixs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.instructions&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;amount_borrowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;borrow_ix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_instruction_at_checked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ixs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;borrowed_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;8&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="mi"&gt;0u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;borrowed_data&lt;/span&gt;&lt;span class="nf"&gt;.copy_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;borrow_ix&lt;/span&gt;&lt;span class="py"&gt;.data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
            &lt;span class="nn"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_le_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;borrowed_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MissingBorrowIx&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// Calculate 5% fee (500 basis points)&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount_borrowed&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.checked_mul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.checked_div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Overflow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;as&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;let&lt;/span&gt; &lt;span class="n"&gt;repay_amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount_borrowed&lt;/span&gt;
            &lt;span class="nf"&gt;.checked_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fee&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.ok_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ProtocolError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Overflow&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="nf"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;CpiContext&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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.token_program&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;from&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.borrower_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;to&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.protocol_ata&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&lt;/span&gt;&lt;span class="p"&gt;(),&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;ctx&lt;/span&gt;&lt;span class="py"&gt;.accounts.borrower&lt;/span&gt;&lt;span class="nf"&gt;.to_account_info&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="n"&gt;repay_amount&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;msg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Repaid {} tokens (borrowed: {}, fee: {})"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repay_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount_borrowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fee&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;Loan&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(mut)]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;borrower&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="nd"&gt;#[account(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"protocol"&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="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SystemAccount&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;mint&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;Mint&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_if_needed,&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;borrower,&lt;/span&gt;
        &lt;span class="nd"&gt;associated_token::mint&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;mint,&lt;/span&gt;
        &lt;span class="nd"&gt;associated_token::authority&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;borrower,&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;borrower_ata&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;TokenAccount&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;associated_token::mint&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;mint,&lt;/span&gt;
        &lt;span class="nd"&gt;associated_token::authority&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;protocol,&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;protocol_ata&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;TokenAccount&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(address&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;INSTRUCTIONS_SYSVAR_ID)]&lt;/span&gt;
    &lt;span class="cd"&gt;/// CHECK: InstructionsSysvar account&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UncheckedAccount&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;token_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;Token&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;associated_token_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;AssociatedToken&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="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;ProtocolError&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;"Invalid instruction"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidIx&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;"Invalid instruction index"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidInstructionIndex&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;"Invalid amount"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidAmount&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;"Not enough funds"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;NotEnoughFunds&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;"Program Mismatch"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;ProgramMismatch&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;"Invalid program"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidProgram&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;"Invalid borrower ATA"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidBorrowerAta&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;"Invalid protocol ATA"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InvalidProtocolAta&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;"Missing repay instruction"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;MissingRepayIx&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;"Missing borrow instruction"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;MissingBorrowIx&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;"Overflow"&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;



</description>
      <category>web3</category>
      <category>rust</category>
      <category>solana</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
