<?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: Okoli Evans</title>
    <description>The latest articles on Forem by Okoli Evans (@okolievans).</description>
    <link>https://forem.com/okolievans</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%2F897599%2F52de8156-028c-40f5-87ab-f568f8a3fa7a.JPG</url>
      <title>Forem: Okoli Evans</title>
      <link>https://forem.com/okolievans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/okolievans"/>
    <language>en</language>
    <item>
      <title>The Controversy of On-Chain Privacy: Monero, Tornado Cash, and Strk20</title>
      <dc:creator>Okoli Evans</dc:creator>
      <pubDate>Tue, 14 Apr 2026 07:59:53 +0000</pubDate>
      <link>https://forem.com/okolievans/the-controversy-of-on-chain-privacy-monero-tornado-cash-and-strk20-2i7n</link>
      <guid>https://forem.com/okolievans/the-controversy-of-on-chain-privacy-monero-tornado-cash-and-strk20-2i7n</guid>
      <description>&lt;p&gt;Privacy onchain has never just been a technical problem, it's more a political one.&lt;/p&gt;

&lt;p&gt;I've been in this space long enough to have watched the same cycle repeat itself: a team ships something that gives users genuine financial privacy, regulators decide it's a money laundering vehicle, exchanges delist it or the developer gets arrested, and the whole thing becomes a cautionary tale. Meanwhile the underlying need — the legitimate need for financial confidentiality — remains. It just waits for the next builder who is brave enough to try.&lt;/p&gt;

&lt;p&gt;This article is about that cycle. About three different bets on how to solve on-chain privacy, what each one got right, what it got wrong, and why the most recent entry — STRK20, a privacy protocol published by StarkWare researchers in March 2026 — might be the first design that actually survives contact with the real world.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Controversy in Plain Terms
&lt;/h2&gt;

&lt;p&gt;Now the irony of the whole thing is that public blockchain is a globally readable ledger. Every amount, every sender, every receiver is permanently visible to anyone who looks. I can't attribute this to design flaw or failure, it's just the natural evolution process of most things; first make it work, refine later.&lt;/p&gt;

&lt;p&gt;The case for financial privacy isn't radical: your salary is your business, your business treasury is your business, your savings are your business.&lt;/p&gt;

&lt;p&gt;But the moment you build something that actually delivers that privacy on-chain, you inherit a problem. The same tool that protects a Nigerian entrepreneur from a predatory counterparty also protects a black hat trying to move stolen funds. Regulators don't distinguish easily between the two, and that ambiguity has defined the entire history of on-chain privacy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Monero: Privacy as Ideology
&lt;/h2&gt;

&lt;p&gt;Monero is the purest expression of the privacy-as-a-right philosophy. Every transaction is private by default, with no opt-out and no exceptions. The engineering behind it is really impressive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ring signatures blend your spend with several decoy inputs to obscure the sender.&lt;/li&gt;
&lt;li&gt;Stealth addresses generate a fresh one-time address per recipient&lt;/li&gt;
&lt;li&gt;RingCT hides amounts completely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Monero does have view keys — a mechanism that lets you voluntarily share your incoming transaction history with a third party like a tax authority or auditor. So it's not a total black box. But the compliance surface is narrow and critically flawed: outgoing transactions are much harder to prove, no auditing entity is baked into the protocol, and there's zero enforcement that users register or share anything. &lt;/p&gt;

&lt;p&gt;Compliance is entirely optional, entirely user-initiated, and entirely unverifiable at scale.&lt;/p&gt;

&lt;p&gt;The ideology is coherent. The problem is what it looks like from the outside.&lt;/p&gt;

&lt;p&gt;When regulators see a system where compliance is possible in theory but structurally unenforceable in practice, they don't give it credit for the theory. They see a system they can't audit, can't subpoena, and can't work with under any meaningful circumstances. &lt;/p&gt;

&lt;p&gt;The response was predictable: systematic delistings. &lt;/p&gt;

&lt;p&gt;Major exchanges removed it. Some jurisdictions moved to ban it outright. Today, Monero survives with a loyal community — but largely at the margins.&lt;/p&gt;

&lt;p&gt;The incomplete compliance path wasn't a strength, it was a ceiling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tornado Cash: When Code Became Criminal
&lt;/h2&gt;

&lt;p&gt;Tornado Cash took a different approach. It wasn't a new chain — it was a smart contract on Ethereum. &lt;/p&gt;

&lt;p&gt;Users deposited fixed denominations of ETH and ERC-20 tokens — DAI, USDC, WBTC — and later withdrew to a new address, breaking the on-chain link using zero-knowledge proofs.&lt;/p&gt;

&lt;p&gt;Simple. Elegant. Effective.&lt;/p&gt;

&lt;p&gt;Then in August 2022, the US Treasury sanctioned it.&lt;br&gt;
Not a company.&lt;br&gt;
Not a person.&lt;br&gt;
A piece of code.&lt;/p&gt;

&lt;p&gt;Interacting with it became legally risky. Its GitHub was taken down. Roman Storm, one of its core developers, was arrested and charged with money laundering conspiracy, sanctions violations, and operating an unlicensed money transmitting business.&lt;/p&gt;

&lt;p&gt;The industry responded with "code is not a crime." &lt;/p&gt;

&lt;p&gt;And philosophically, that argument matters — holding a developer criminally liable for how users choose to use open-source software sets a disturbing precedent that the whole industry should be paying attention to.&lt;/p&gt;

&lt;p&gt;But from a product perspective, Tornado had the same structural problem as Monero:&lt;br&gt;
No compliance path.&lt;br&gt;
No mechanism — under any circumstance, for any authorized party — to trace funds through it. &lt;/p&gt;

&lt;p&gt;That wasn't just a philosophical position. It was a design decision that handed regulators a clean narrative and prosecutors a clean case.&lt;/p&gt;




&lt;h2&gt;
  
  
  STRK20: A Different Bet Entirely
&lt;/h2&gt;

&lt;p&gt;STRK20 starts from a different premise:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The real issue isn't privacy. It's accountability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you can build a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy is the default
&lt;/li&gt;
&lt;li&gt;But accountability is possible under legitimate legal processes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…then you remove the core argument regulators have used against every previous system.&lt;/p&gt;

&lt;p&gt;That’s the bet.&lt;/p&gt;




&lt;h2&gt;
  
  
  How STRK20 Actually Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Note Model
&lt;/h3&gt;

&lt;p&gt;Like Zcash and Aztec, STRK20 uses a UTXO-style “note” system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each note encodes owner, token, and amount
&lt;/li&gt;
&lt;li&gt;Transfers = spending notes + creating new ones
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Standard foundation. The differentiation is everything built around it.&lt;/p&gt;




&lt;h3&gt;
  
  
  Nullifiers Without Commitments
&lt;/h3&gt;

&lt;p&gt;When a note is spent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A nullifier marks it as used
&lt;/li&gt;
&lt;li&gt;The actual note identity remains hidden
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike Zcash:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No Merkle tree of commitments
&lt;/li&gt;
&lt;li&gt;Notes live directly in Starknet contract storage (encrypted)
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Reduces proof complexity
&lt;/li&gt;
&lt;li&gt;Lowers storage overhead
&lt;/li&gt;
&lt;li&gt;Leans into Starknet’s architecture
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Channel-Based Discovery (Key Innovation)
&lt;/h3&gt;

&lt;p&gt;Traditional problem (Zcash):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You must scan and attempt to decrypt &lt;em&gt;everything&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Complexity: &lt;strong&gt;O(total transactions)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;STRK20 flips this.&lt;/p&gt;

&lt;p&gt;Notes are stored at locations derived from a shared secret between sender and recipient.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The recipient already knows where to look
&lt;/li&gt;
&lt;li&gt;No global scanning
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Channels → Subchannels → Notes
&lt;/li&gt;
&lt;li&gt;Sequential indexing enforced (no gaps allowed)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Discovery cost becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;O(your activity)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not the network’s.&lt;/p&gt;

&lt;p&gt;That’s a massive scalability unlock.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Compliance Framework
&lt;/h3&gt;

&lt;p&gt;This is the controversial core.&lt;/p&gt;

&lt;p&gt;When registering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user encrypts their viewing key to an auditing entity
&lt;/li&gt;
&lt;li&gt;The encrypted key is stored on-chain
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The auditing entity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses a threshold system (multi-party control)
&lt;/li&gt;
&lt;li&gt;Can decrypt viewing keys only under valid legal request
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trace incoming and outgoing transactions
&lt;/li&gt;
&lt;li&gt;Perform forward and backward tracing
&lt;/li&gt;
&lt;li&gt;Only expose data for the target user
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important constraint:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No leakage about unrelated users
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sequential indexing ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No hidden transactions
&lt;/li&gt;
&lt;li&gt;No skipping records
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have the viewing key, you see everything for that user.&lt;/p&gt;




&lt;h3&gt;
  
  
  Multi-Asset + DeFi Integration
&lt;/h3&gt;

&lt;p&gt;Most previous privacy systems built a separate pool for each asset.&lt;/p&gt;

&lt;p&gt;Tornado Cash had distinct contracts for ETH, DAI, USDC. &lt;/p&gt;

&lt;p&gt;STRK20 runs everything through a single pool — multiple token types, one contract. Simpler architecture, better anonymity set, less fragmentation.&lt;/p&gt;

&lt;p&gt;The DeFi integration is where it gets genuinely clever. &lt;/p&gt;

&lt;p&gt;The fundamental problem with using a privacy protocol inside a DEX is timing: your ZK proof has to be generated before the transaction executes, but AMM swap outputs depend on pool state at execution time. The pool might shift between when you generate the proof and when it settles. You can't commit to a number you don't know yet.&lt;/p&gt;

&lt;p&gt;STRK20 solves this with open notes — a special note type that starts empty and gets filled by an external contract after the swap executes. &lt;/p&gt;

&lt;p&gt;The user's proof creates a placeholder note for the destination token with zero amount. The helper contract performs the swap, receives the output, and deposits it into that pre-registered note. The whole flow is atomic — it either all happens or none of it does.&lt;/p&gt;

&lt;p&gt;The privacy tradeoff is honest and minimal: swap amounts and token types are visible on-chain, but the AMM's public state would have revealed that anyway. What stays hidden is the only thing that matters — who made the swap.&lt;/p&gt;




&lt;h3&gt;
  
  
  Unified Cairo Codebase
&lt;/h3&gt;

&lt;p&gt;Most ZK systems split:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Circuit logic (Circom/Noir)
&lt;/li&gt;
&lt;li&gt;Smart contract logic (Solidity)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;STRK20 uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cairo for everything&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks to StarkWare’s prover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ZK proof = proof of Cairo execution
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One language
&lt;/li&gt;
&lt;li&gt;One logic system
&lt;/li&gt;
&lt;li&gt;Easier auditing
&lt;/li&gt;
&lt;li&gt;Shared infrastructure with Starknet
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparing the Three
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Monero&lt;/th&gt;
&lt;th&gt;Tornado Cash&lt;/th&gt;
&lt;th&gt;STRK20&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;Strong, default&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Strong&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compliance&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discovery&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;O(user activity)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-asset&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeFi&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survival&lt;/td&gt;
&lt;td&gt;Marginalized&lt;/td&gt;
&lt;td&gt;Sanctioned&lt;/td&gt;
&lt;td&gt;Designed to coexist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Model&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Split&lt;/td&gt;
&lt;td&gt;Unified&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Real Controversy STRK20 Surfaces
&lt;/h2&gt;

&lt;p&gt;STRK20 doesn't eliminate the debate. It reframes it.&lt;br&gt;
The criticism is legitimate: there is a trusted auditing entity, a master decryption capability exists, and that is a centralisation point with no equivalent in Monero or the original Tornado Cash. Privacy purists will call this a betrayal of the principle. I understand that position.&lt;/p&gt;

&lt;p&gt;But I keep coming back to the alternative. Not the theoretical alternative — the actual, documented one. Delistings. Sanctions. A developer facing decades in prison for writing open-source code. &lt;/p&gt;

&lt;p&gt;Pure privacy systems haven't survived regulatory pressure, and surviving isn't optional if you want anyone beyond a niche to actually use what you build.&lt;/p&gt;

&lt;p&gt;Bringing the billions onchain goes beyond a small niche of users to the open society, including individuals, institutions, organizations and so on.&lt;/p&gt;

&lt;p&gt;So the question STRK20 forces you to answer is honest and uncomfortable in equal measure:&lt;/p&gt;

&lt;p&gt;Is privacy that works for millions better than privacy that works for a few?&lt;/p&gt;

&lt;p&gt;That's a values question as much as a technical one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;This debate was never really about cryptography. It's about power — who gets to see your money, when, and under whose authority.&lt;/p&gt;

&lt;p&gt;Monero said nobody. Tornado Cash said nobody. Both paid for it in ways that are still unfolding.&lt;/p&gt;

&lt;p&gt;STRK20 says something more precise: nobody, unless the law specifically requires it, and even then only the person under investigation. &lt;/p&gt;

&lt;p&gt;Not their neighbours in the pool. &lt;br&gt;
Not their counterparties. &lt;br&gt;
Just them.&lt;/p&gt;

&lt;p&gt;That's not ideological purity. But ideology doesn't keep protocols alive — and dead protocols don't protect anyone's privacy.&lt;/p&gt;

&lt;p&gt;And honestly? The compliance argument isn't just about regulatory survival. We've seen what unaccountable financial privacy looks like in practice — ransomware payouts, sanctions evasion, illicit financing moving through mixers at scale. Those aren't hypotheticals. They're documented history. Dismissing that to win a philosophical argument is its own kind of dishonesty.&lt;/p&gt;

&lt;p&gt;The strongest case for STRK20 isn't that it compromises on privacy. It's that it takes both things seriously at once — the individual's right to financial confidentiality, and society's legitimate interest in accountability when the law demands it. Not one at the expense of the other.&lt;/p&gt;

&lt;p&gt;That balance is harder to build. It's also harder to tear down.&lt;/p&gt;

&lt;p&gt;But this is just my own view, I'm eager to hear what everyone thinks... leave a comment or tweet at me &lt;a href="https://x.com/OkoliEvans" rel="noopener noreferrer"&gt;https://x.com/OkoliEvans&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Cheerio...&lt;/p&gt;

&lt;p&gt;Resouces:&lt;br&gt;
&lt;a href="https://www.starknet.io/blog/make-all-erc-20-tokens-private-with-strk20/" rel="noopener noreferrer"&gt;https://www.starknet.io/blog/make-all-erc-20-tokens-private-with-strk20/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://eprint.iacr.org/2026/474" rel="noopener noreferrer"&gt;https://eprint.iacr.org/2026/474&lt;/a&gt;&lt;/p&gt;

</description>
      <category>starknet</category>
      <category>web3</category>
      <category>privacy</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>AVNU Paymaster &amp; Gas Sponsorship on Starknet</title>
      <dc:creator>Okoli Evans</dc:creator>
      <pubDate>Sun, 22 Mar 2026 23:24:52 +0000</pubDate>
      <link>https://forem.com/okolievans/avnu-paymaster-gas-sponsorship-on-starknet-425f</link>
      <guid>https://forem.com/okolievans/avnu-paymaster-gas-sponsorship-on-starknet-425f</guid>
      <description>&lt;h2&gt;
  
  
  A Practical Guide for Building Gasless dApps
&lt;/h2&gt;




&lt;h2&gt;
  
  
  PART ONE
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Understanding AVNU Paymaster
&lt;/h2&gt;




&lt;p&gt;Imagine this:&lt;/p&gt;

&lt;p&gt;You're a normal Web2 user.&lt;/p&gt;

&lt;p&gt;You've been hearing about Web3 for a while, and you finally decide to try it out.&lt;/p&gt;

&lt;p&gt;You find a cool game.&lt;/p&gt;

&lt;p&gt;The UI looks clean. It feels legit for a gamer.&lt;/p&gt;

&lt;p&gt;So you go through the process:&lt;/p&gt;

&lt;p&gt;You install MetaMask.&lt;br&gt;
You follow a YouTube tutorial.&lt;br&gt;
You create your wallet.&lt;/p&gt;

&lt;p&gt;You come back, connect your wallet, and click the main button:&lt;/p&gt;

&lt;p&gt;"Start"&lt;/p&gt;

&lt;p&gt;And then you see this:&lt;/p&gt;

&lt;p&gt;"Insufficient ETH for gas fees."&lt;/p&gt;

&lt;p&gt;You pause.&lt;/p&gt;

&lt;p&gt;What is gas?&lt;br&gt;
Why do I need ETH?&lt;br&gt;
I just want to start my game…&lt;/p&gt;

&lt;p&gt;At this point, most users don't go buy ETH.&lt;/p&gt;

&lt;p&gt;They leave.&lt;/p&gt;



&lt;p&gt;This is the &lt;strong&gt;gas fee problem&lt;/strong&gt; — and it quietly kills Web3 adoption.&lt;/p&gt;

&lt;p&gt;No matter how good your product is, requiring users to hold a native token before doing anything is friction most won't tolerate, especially new web3 users.&lt;/p&gt;


&lt;h2&gt;
  
  
  On Starknet, AVNU Paymaster has successfully fixed this problem.
&lt;/h2&gt;

&lt;p&gt;Built on Starknet's account abstraction model, it allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your dApp to &lt;strong&gt;sponsor gas fees entirely for users&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Or users to &lt;strong&gt;pay fees in supported tokens (like USDC, USDT)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No STRK required for normal transactions.&lt;br&gt;
No onboarding friction.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Is a Paymaster?
&lt;/h2&gt;

&lt;p&gt;A Paymaster is a smart contract that handles transaction fees on behalf of a user. Users do not pay gas fees — the fees are paid by a sponsor on behalf of the user.&lt;/p&gt;

&lt;p&gt;This unlocks a UX potential for dApps running on Starknet. Coupled with native account abstraction on Starknet, builders can now achieve web2 level of UX in their applications.&lt;/p&gt;


&lt;h2&gt;
  
  
  PART TWO
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Paymaster Setup
&lt;/h2&gt;



&lt;ol&gt;
&lt;li&gt;Setup paymaster account and create API key on AVNU portal.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://portal.avnu.fi/" rel="noopener noreferrer"&gt;https://portal.avnu.fi/&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on connect wallet and sign in&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create your account&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The next page will require you to enter your name and your contact details, fill in the required details accordingly. Once you are done, click on &lt;code&gt;create account&lt;/code&gt; and it takes you straight to your dashboard:&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%2Fsdxkrud8n6g0w622es75.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%2Fsdxkrud8n6g0w622es75.png" alt="create api key" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Create Your First Key&lt;/code&gt; to create your API key. Enter your project name (can be anything) and click on &lt;code&gt;create key&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you got everything right, you'll see this page:&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%2Fpvg6lw5a4w41b3sw48g2.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%2Fpvg6lw5a4w41b3sw48g2.png" alt="API key success" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Click on &lt;code&gt;add credits&lt;/code&gt; to fund your paymaster account. The credit is what is actually used to make payments on behalf of your users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor your dashboard&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once live, you can track everything from the dashboard — gas sponsored, success rate, number of unique accounts sponsored, burn rate, etc.&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%2F6y615xi1raw78rlftjsl.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%2F6y615xi1raw78rlftjsl.png" alt="user dashboard" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have completed the paymaster setup, next is to show how you can integrate it in your dApp.&lt;/p&gt;


&lt;h2&gt;
  
  
  PART THREE
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Integration Guide
&lt;/h2&gt;


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

&lt;ul&gt;
&lt;li&gt;Node.js (v18+)&lt;/li&gt;
&lt;li&gt;JavaScript / TypeScript project&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Step 1 — Install Dependencies
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;PaymasterRpc&lt;/code&gt; class is imported directly from the &lt;code&gt;starknet&lt;/code&gt; package. If you don't have it installed yet, add it to your project:&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;starknet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Step 2 — Initialise the Paymaster
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;PaymasterRpc&lt;/code&gt; class is your connection to the AVNU Paymaster service. Initialise it with your chosen network endpoint and API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PaymasterRpc&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;starknet&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;paymaster&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;PaymasterRpc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;nodeUrl&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://starknet.paymaster.avnu.fi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// mainnet&lt;/span&gt;
  &lt;span class="c1"&gt;// nodeUrl: 'https://sepolia.paymaster.avnu.fi', // testnet (free)&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;x-paymaster-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AVNU_API_KEY&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;
  
  
  Step 3 — Execute a Sponsored Transaction
&lt;/h3&gt;

&lt;p&gt;Pass the paymaster into the &lt;code&gt;Account&lt;/code&gt; constructor, then call &lt;code&gt;executePaymasterTransaction()&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RpcProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PaymasterRpc&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;starknet&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;account&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;Account&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;address&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;ACCOUNT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;signer&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;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;paymaster&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executePaymasterTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;feeMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sponsored&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// You sponsor, user pays nothing&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ No gas required from the user&lt;br&gt;
✅ Paid from your AVNU credits&lt;/p&gt;


&lt;h3&gt;
  
  
  Step 4 — Secure Your API Key (Server Proxy)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Never expose your API key in frontend code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a server-side proxy that forwards requests to AVNU with your key attached. Here's a Next.js example:&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;// app/api/paymaster/route.ts&lt;/span&gt;
&lt;span class="k"&gt;export&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;POST&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;Request&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;body&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;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;response&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;fetch&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://starknet.paymaster.avnu.fi&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&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="s1"&gt;application/json&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="s1"&gt;x-paymaster-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AVNU_API_KEY&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="na"&gt;body&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;body&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="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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's the same pattern in an Express backend — this is taken directly from &lt;a href="https://zapcodex.netlify.app" rel="noopener noreferrer"&gt;Zapcode&lt;/a&gt;'s production &lt;code&gt;wallet.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;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;response&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AVNU_PAYMASTER_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;avnuHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// includes x-paymaster-api-key, stored server-side&lt;/span&gt;
      &lt;span class="na"&gt;body&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;data&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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;[paymaster]&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&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;json&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;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="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 5 — Use the Proxy on the Client
&lt;/h3&gt;

&lt;p&gt;Point your &lt;code&gt;PaymasterRpc&lt;/code&gt; to your own proxy route instead of directly to AVNU — your API key never touches the browser:&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;paymaster&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;PaymasterRpc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;nodeUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/paymaster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// your proxy route&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Full Working Code Example
&lt;/h2&gt;

&lt;p&gt;Here's a complete, annotated JavaScript file combining all the steps above. Replace the placeholder values with your own before running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// avnu-paymaster-example.js&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;Account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RpcProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PaymasterRpc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;starknet&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Set up the Starknet RPC provider&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&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;RpcProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// For Sepolia testnet: "https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_10/YOUR_ALCHEMY_KEY"&lt;/span&gt;
    &lt;span class="na"&gt;nodeUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://starknet-mainnet.g.alchemy.com/starknet/version/rpc/v0_10/YOUR_ALCHEMY_KEY&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;// 2. Initialise the AVNU Paymaster&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymaster&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;PaymasterRpc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// For Sepolia testnet: "https://sepolia.paymaster.avnu.fi"&lt;/span&gt;
    &lt;span class="na"&gt;nodeUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://starknet.paymaster.avnu.fi&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;x-paymaster-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AVNU_API_KEY&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;// 3. Set up account — paymaster is passed in the constructor&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&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;ACCOUNT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;signer&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;PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;paymaster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Define the contract calls to execute (we use STRK for demo)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STRK_ADDRESS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d&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;calls&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="na"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;STRK_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transfer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;calldata&lt;/span&gt;&lt;span class="p"&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;0xRECIPIENT_ADDRESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// recipient wallet address&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1000000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// amount (STRK has 18 decimals — adjust accordingly)&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&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;// 5. Execute — user pays nothing, your AVNU credits cover the gas&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Submitting sponsored transaction...&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executePaymasterTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;feeMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sponsored&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transaction hash:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction_hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// 6. Wait for on-chain confirmation&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;retryInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;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;main&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;console&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;AVNU_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_api_key_from_portal
&lt;span class="nv"&gt;ACCOUNT_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xYOUR_STARKNET_ACCOUNT_ADDRESS
&lt;span class="nv"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0xYOUR_PRIVATE_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node avnu-paymaster-example.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing &amp;amp; Verifying Your Integration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Test on Sepolia first&lt;/strong&gt; — switch your &lt;code&gt;PaymasterRpc&lt;/code&gt; nodeUrl to &lt;code&gt;sepolia.paymaster.avnu.fi&lt;/code&gt;. Credits are unlimited and free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check your dashboard&lt;/strong&gt; — after running a transaction, go to your AVNU Portal explorer on your dashboard. You should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The transaction hash appear in your logs&lt;/li&gt;
&lt;li&gt;The gas cost deducted (on Sepolia this is simulated — no real cost)&lt;/li&gt;
&lt;li&gt;Transaction status: confirmed or failed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Common errors:&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;Error&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;401 Unauthorised&lt;/td&gt;
&lt;td&gt;API key is missing or incorrect — check your &lt;code&gt;.env&lt;/code&gt; file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account not compatible&lt;/td&gt;
&lt;td&gt;Your wallet must be Ready (ArgentX) or Braavos and deployed on-chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insufficient credits&lt;/td&gt;
&lt;td&gt;Top up your credit balance in the AVNU Portal (mainnet only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction rejected&lt;/td&gt;
&lt;td&gt;Check your calldata and contract address are correct&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;You now have everything you need to integrate AVNU Paymaster into your Starknet dApp. To recap what you've built:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up your account and API key in the AVNU Portal&lt;/li&gt;
&lt;li&gt;Initialised the &lt;code&gt;PaymasterRpc&lt;/code&gt; provider with starknet.js&lt;/li&gt;
&lt;li&gt;Executed a fully sponsored transaction — gas paid by your dApp&lt;/li&gt;
&lt;li&gt;Set up a secure server-side proxy to protect your API key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Gas sponsorship is one part of the AVNU paymaster guide. In the next guide, we'll cover the second mode — letting users pay gas in any token (USDC, USDT, LORDS, wstETH, and more) — which opens up entirely new possibilities for builders on Starknet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AVNU Portal — &lt;a href="https://portal.avnu.fi" rel="noopener noreferrer"&gt;portal.avnu.fi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AVNU Paymaster Docs — &lt;a href="https://docs.avnu.fi/docs/paymaster" rel="noopener noreferrer"&gt;docs.avnu.fi/docs/paymaster&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;starknet.js — &lt;a href="https://starknetjs.com" rel="noopener noreferrer"&gt;starknetjs.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub — &lt;a href="https://github.com/avnu-labs/paymaster" rel="noopener noreferrer"&gt;github.com/avnu-labs/paymaster&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For questions or feedback, kindly leave a comment or send a message at &lt;a href="https://x.com/okolievans" rel="noopener noreferrer"&gt;https://x.com/okolievans&lt;/a&gt;&lt;/p&gt;

</description>
      <category>starknet</category>
      <category>paymaster</category>
      <category>avnu</category>
      <category>web3</category>
    </item>
    <item>
      <title>From Web2 to Web3: A Practical Guide to Building a Gasless Crypto Payment Flow (With Starknet &amp; StarkZap)</title>
      <dc:creator>Okoli Evans</dc:creator>
      <pubDate>Thu, 19 Mar 2026 10:58:54 +0000</pubDate>
      <link>https://forem.com/okolievans/from-web2-to-web3-a-practical-guide-to-building-a-gasless-crypto-payment-flow-with-starknet--1l6f</link>
      <guid>https://forem.com/okolievans/from-web2-to-web3-a-practical-guide-to-building-a-gasless-crypto-payment-flow-with-starknet--1l6f</guid>
      <description>&lt;h2&gt;
  
  
  A hands-on guide for React/Node developers to integrate wallets, social login, and USDC payments — without learning blockchain from scratch or writing many lines of code.
&lt;/h2&gt;




&lt;h2&gt;
  
  
  Why Most Web2 Developers Get Stuck trying to build on Web3
&lt;/h2&gt;

&lt;p&gt;Most Web2 developers coming into Web3 hit the same wall — and usually with very little guidance.&lt;/p&gt;

&lt;p&gt;You start with the docs, and immediately run into a wall of jargon: &lt;em&gt;nonces, calldata, account abstraction, gas estimation&lt;/em&gt;. Before long, you’re three hours deep in a Stack Overflow rabbit hole wondering why you didn’t just use Stripe.&lt;/p&gt;

&lt;p&gt;This guide is for developers who already know React, Node.js, or any backend stack — but haven’t really touched Web3 beyond reading about it or seeing monkey pictures on X.&lt;/p&gt;

&lt;p&gt;By the end of this article, you’ll have built a &lt;strong&gt;complete Web3 payment flow&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Social login (email / Google)
&lt;/li&gt;
&lt;li&gt;Embedded wallets (no seed phrases)
&lt;/li&gt;
&lt;li&gt;Gasless transactions
&lt;/li&gt;
&lt;li&gt;On-chain USDC payments
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And you’ll do it &lt;strong&gt;without needing to understand zero-knowledge proofs or low-level blockchain concepts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Everything here comes from building &lt;strong&gt;Zapcode&lt;/strong&gt; — a real QR-based USDC payment system on Starknet. These are production patterns, not toy examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Starknet (And Why It Matters)
&lt;/h2&gt;

&lt;p&gt;Starknet is a &lt;strong&gt;Layer 2 scaling network on Ethereum&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s what matters in practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transactions are &lt;strong&gt;fast&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Fees are &lt;strong&gt;extremely low (fractions of a cent)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Security is still backed by Ethereum
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Under the hood, Starknet batches transactions and proves them on Ethereum using zero-knowledge proofs. But you don’t need to think about that.&lt;/p&gt;

&lt;p&gt;As a developer, you can treat it like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Ethereum — but more advanced and more scalable.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is StarkZap?
&lt;/h2&gt;

&lt;p&gt;StarkZap is an SDK that sits on top of Starknet and abstracts the hardest parts of Web3 development. One command to rule them all: &lt;code&gt;npm install starkzap&lt;/code&gt;, to install Starkzap package.&lt;/p&gt;

&lt;p&gt;Instead of dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raw calldata
&lt;/li&gt;
&lt;li&gt;Account deployment
&lt;/li&gt;
&lt;li&gt;Signing flows
&lt;/li&gt;
&lt;li&gt;Gas mechanics
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get simple APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What StarkZap Gives You
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wallet creation&lt;/strong&gt; — no seed phrases required
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account deployment&lt;/strong&gt; — handled automatically
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ERC20 transfers&lt;/strong&gt; — send USDC with a clean API
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fee sponsorship&lt;/strong&gt; — users pay zero gas via AVNU
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social login&lt;/strong&gt; — email / Google login via Privy
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitcoin support&lt;/strong&gt; — via Starknet cross-chain infrastructure
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Staking module&lt;/strong&gt; — for DeFi-style apps
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one — social login — is what changes everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making Crypto Feel Like Web2: Social Login
&lt;/h2&gt;

&lt;p&gt;The biggest UX problem in Web3 is wallet setup.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Install MetaMask. Save these 12 words. Don’t lose them.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most users drop off right there.&lt;/p&gt;

&lt;p&gt;Privy fixes this.&lt;/p&gt;

&lt;p&gt;Users log in with email or Google, and a wallet is created for them behind the scenes. From their perspective, it feels like any normal app.&lt;/p&gt;

&lt;p&gt;Here’s how we set it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install privy: &lt;code&gt;npm install @privy-io/react-auth&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/frontend/src/main.tsx&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;PrivyProvider&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;@privy-io/react-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PrivyProvider&lt;/span&gt;
  &lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="o"&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_PRIVY_APP_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
    &lt;span class="na"&gt;loginMethods&lt;/span&gt;&lt;span class="p"&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;email&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="s1"&gt;google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;appearance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&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="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;PrivyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Backend Setup (Important Gotcha)
&lt;/h2&gt;

&lt;p&gt;You need &lt;strong&gt;two Privy clients&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Installation command: &lt;code&gt;npm install @privy-io/server-auth @privy-io/node&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/backend/src/lib/privy.js&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;PrivyClient&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;@privy-io/server-auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PrivyNode&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;@privy-io/node&lt;/span&gt;&lt;span class="dl"&gt;'&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;privy&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;PrivyClient&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;PRIVY_APP_ID&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;PRIVY_APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&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;privyNode&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;PrivyNode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;appId&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;PRIVY_APP_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;appSecret&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;PRIVY_APP_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;authorizationKey&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;PRIVY_AUTHORIZATION_KEY&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 distinction matters more than it looks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;server-auth&lt;/code&gt; → authentication
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node&lt;/code&gt; → wallet operations &amp;amp; signing
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you mix them, things break in non-obvious ways.&lt;/p&gt;




&lt;h2&gt;
  
  
  Onboarding a User Wallet
&lt;/h2&gt;

&lt;p&gt;When a user scans a QR code in Zapcode, we need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a wallet
&lt;/li&gt;
&lt;li&gt;Fund it (for deployment)
&lt;/li&gt;
&lt;li&gt;Deploy it on-chain
&lt;/li&gt;
&lt;li&gt;Make it usable
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;StarkZap handles most of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/frontend/src/pages/PayPage.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&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;StarkZap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mainnet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;paymaster&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nodeUrl&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;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/wallet/paymaster`&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;onboard&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;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onboard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="nx"&gt;OnboardStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Privy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accountPreset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accountPresets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argentXV050&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;if_needed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;feeMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_pays&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;privy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&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;getAccessToken&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;res&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;fetch&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;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/wallet/starknet`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;w&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;serverUrl&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;API_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/wallet/sign`&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="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key part:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;deploy: 'if_needed'&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First-time users get deployed. After that, it’s invisible.&lt;/p&gt;




&lt;h2&gt;
  
  
  The One Catch: Account Deployment Costs
&lt;/h2&gt;

&lt;p&gt;AVNU sponsors transactions — but &lt;strong&gt;not account deployment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;New wallets must pay a small STRK fee.&lt;/p&gt;

&lt;h3&gt;
  
  
  How We Solved It
&lt;/h3&gt;

&lt;p&gt;We use a treasury wallet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BUYER_PREFUND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;300000000000000000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 0.3 STRK&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendStrk&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;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BUYER_PREFUND&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;buyer&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;This is a one-time cost per user.&lt;/p&gt;

&lt;p&gt;After that → &lt;strong&gt;everything is gasless&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sending a USDC Payment
&lt;/h2&gt;

&lt;p&gt;Here’s the actual payment logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&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;walletRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="na"&gt;contractAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;USDC_MAINNET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transfer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;calldata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;merchant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;walletAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&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;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parsed&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&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;feeMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sponsored&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;From the user’s perspective:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Click → Pay → Done&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No gas. No wallets. No confusion.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gasless Transactions (How It Works)
&lt;/h2&gt;

&lt;p&gt;AVNU’s paymaster covers gas fees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&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;fetch&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://starknet.paymaster.avnu.fi&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&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;Content-Type&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="s1"&gt;application/json&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="s1"&gt;x-paymaster-api-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AVNU_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&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;data&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Users don’t need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ETH
&lt;/li&gt;
&lt;li&gt;STRK
&lt;/li&gt;
&lt;li&gt;Any understanding of gas
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This removes one of the biggest barriers in Web3 UX.&lt;/p&gt;




&lt;h2&gt;
  
  
  Server-Side Signing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sign&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;privyNode&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wallets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rawSign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walletId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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;StarkZap handles calling this automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Flow (Zapcode)
&lt;/h2&gt;

&lt;p&gt;Here’s what the full system looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merchant signs up → gets wallet + QR code
&lt;/li&gt;
&lt;li&gt;Buyer scans → logs in with email
&lt;/li&gt;
&lt;li&gt;Wallet created + deployed automatically
&lt;/li&gt;
&lt;li&gt;Buyer enters amount → clicks Pay
&lt;/li&gt;
&lt;li&gt;USDC transfers instantly (gasless)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without users knowing anything about crypto.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Pitfalls (Learned the Hard Way)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Starknet.js v9 changed Account constructor
&lt;/li&gt;
&lt;li&gt;You must use two Privy clients
&lt;/li&gt;
&lt;li&gt;Wallet ownership depends on your authorization key
One way to avoid silent errors is to use the latest versions of the packages.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts: Web3 Is Finally Usable
&lt;/h2&gt;

&lt;p&gt;You don’t need to understand zero-knowledge proofs to build on Starknet.&lt;/p&gt;

&lt;p&gt;You don’t need to know what account abstraction means to ship a real product.&lt;/p&gt;

&lt;p&gt;StarkZap handles the complexity — you focus on the experience.&lt;/p&gt;

&lt;p&gt;If you’re a Web2 developer who’s been curious about Web3 but kept bouncing off the complexity wall:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is your way in.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The tooling is ready.&lt;br&gt;&lt;br&gt;
The UX is finally good enough and getting better.&lt;br&gt;&lt;br&gt;
You can ship real applications now in just a few lines of code. Use Starkzap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Zapcode Live: &lt;a href="https://zapcodex.netlify.app/" rel="noopener noreferrer"&gt;https://zapcodex.netlify.app/&lt;/a&gt;&lt;br&gt;
Zapcode Github: &lt;a href="https://github.com/OkoliEvans/zapcode" rel="noopener noreferrer"&gt;https://github.com/OkoliEvans/zapcode&lt;/a&gt;&lt;br&gt;&lt;br&gt;
StarkZap: &lt;a href="https://github.com/keep-starknet-strange/starkzap" rel="noopener noreferrer"&gt;https://github.com/keep-starknet-strange/starkzap&lt;/a&gt;&lt;br&gt;
StarkZap sample codebases and projects: &lt;a href="https://github.com/keep-starknet-strange/awesome-starkzap" rel="noopener noreferrer"&gt;https://github.com/keep-starknet-strange/awesome-starkzap&lt;/a&gt;&lt;br&gt;&lt;br&gt;
AVNU: &lt;a href="https://portal.avnu.fi" rel="noopener noreferrer"&gt;https://portal.avnu.fi&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Privy: &lt;a href="https://privy.io" rel="noopener noreferrer"&gt;https://privy.io&lt;/a&gt;  &lt;/p&gt;




&lt;p&gt;If you build something with StarkZap, drop it below.&lt;/p&gt;

&lt;p&gt;I’d genuinely love to see what you ship.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>react</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>Understanding Digital Signatures: The Role of v, r, s in Cryptographic Security and Signature Verification.</title>
      <dc:creator>Okoli Evans</dc:creator>
      <pubDate>Sun, 11 Jun 2023 20:27:41 +0000</pubDate>
      <link>https://forem.com/okolievans/understanding-digital-signatures-the-role-of-v-r-s-in-cryptographic-security-and-signature-verification-7m5</link>
      <guid>https://forem.com/okolievans/understanding-digital-signatures-the-role-of-v-r-s-in-cryptographic-security-and-signature-verification-7m5</guid>
      <description>&lt;p&gt;Pre-script: This article is not intended for newbies, knowledge of Ethereum and/or solidity is presumed.&lt;/p&gt;

&lt;p&gt;Transactions on Ethereum are signed messages originating from Externally Owned Accounts (EOAs).  There are more to this simple description though, as not all of such messages are valid transactions. Valid transactions must contain these required parameters: Nonce, Recipient, Value, Data, Gas price, Gas limit, Chain Id and v, r, s, and the transaction must be signed using the EOA’s private key (and we’ll see why shortly).&lt;/p&gt;

&lt;p&gt;Understanding the concept of digital signatures without prior understanding of the components of that signature may be a bit much for some people, so we’ll first try to explain some of these concepts (of course these concepts are well detailed in the Ethereum book).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nonce&lt;/strong&gt; is an acronym for Number used ONCE. It’s an integer that keeps track of the number of messages signed and transmitted from an account. It increments per transaction, and a nonce cannot be used more than once. Any transaction signed with an already used nonce is rejected by the network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Value&lt;/strong&gt; is the amount of ether or token forwarded with the message. Transactions containing only values are known as ‘payments’.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data&lt;/strong&gt; is the payload forwarded with the transaction. These are mostly function calls (payload containing function selector) directed to contract accounts, as most EOAs do not contain bytecodes and hence cannot handle such calls. Transactions containing only data are known as ‘invocations’. Aside from these, transactions can also contain both data and value, or nothing at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chain ID&lt;/strong&gt; represents the blockchain network to which the transaction is to be sent. The use of chain id means that transactions created for one blockchain cannot be valid on another blockchain. This protects transactions from replay attacks, as transactions become invalid if sent to another network. (see EIP-155 for more on this).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;v, r, s&lt;/strong&gt; are signature variables generated from EOA’s private keys and used for digital signatures creation and verification. (More on this shortly). &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Idea of Private keys and Public keys&lt;/strong&gt;&lt;br&gt;
Private keys are a secret set of randomly generated integers used in cryptography for creating public keys and signing transactions. These keys are neither required nor transmitted on Ethereum network, they are sole properties and responsibilities of EOAs, and anyone with access to them has access to all the accounts generated from that private key.&lt;/p&gt;

&lt;p&gt;Now, how do we generate public keys from private keys? Magic.&lt;/p&gt;

&lt;p&gt;Ethereum makes use of public key cryptography, also known as asymmetric cryptography, to create public-private key pairs. Elliptic curve cryptography (which is a form of asymmetric cryptography) is used to calculate the public key from the private key in an irreversible equation using the elliptic curve algorithm secp256k1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two things to bear in mind:&lt;/strong&gt;&lt;br&gt;
i) ‘A public key is a point on the elliptic curve, meaning it is a set of x and y coordinates that satisfy the elliptic curve equation’. Hence public keys are two numbers joined together, each number representing a point on the elliptic curve.&lt;/p&gt;

&lt;p&gt;ii) The elliptic curve algorithm used to calculate these points is a one way function. In practice it is very easy to calculate the public key from a private key, but impossible to reverse the process and recover the private key that generated that public key. This means that the algorithm cannot be reversed to get a private key that generated a public key. Not even quantum computers have been able to achieve this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transaction serialization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we understand the concept of public and private keys and how they are generated, let us go a step further to explain how transactions are serialized before they are broadcasted to the network. &lt;/p&gt;

&lt;p&gt;Serialization is the concept of preparing signed messages for broadcast to the Ethereum network by using a particular function (algorithm) to create a particular sequence of bytes. The function used for formatting the transaction is a generally accepted standard on the network, such that transactions formatted with this standard are accepted on the blockchain irrespective of the library, language or application used by any particular node. On the Ethereum network the standard used for this purpose is the RLP encoding algorithm (the Recursive-length prefix algorithm).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concept of Digital Signatures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Digital signatures are synonymous with traditional official signatures. It’s simply a way to authorize a transaction or message on the internet. Any signed transaction contains a digital signature, and the signature is a proof of validity of such message. &lt;/p&gt;

&lt;p&gt;Digital signatures are very important because they provide a proof of the account where a transaction originates from (tx.origin in Solidity), and the signer of such a transaction cannot deny that the signature originated from his account. Digital signatures also provide proof that the content of a message or transaction is not tampered with, because any change in the content of the message will produce a signature entirely different  from the original one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Digital Signatures are Created&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the lower level, digital signatures are generated from Elliptic Curve Digital Signature Algorithm ( ECDSA ), and this algorithm consists of two parts. The first part is the signature creating algorithm, while the second part is the signature verifying algorithm. &lt;/p&gt;

&lt;p&gt;Digital signatures are created when a private key signs an already serialized transaction. Actually the serialized transaction here is the Keccak256 hash of the RLP-encoded message. The mathematical function for signing transaction is:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S i g = Fsig(Fkeccak256(m), k)
where: k = the signing private key
           m= the RLP encoded message
           Fkeccak256 = Keccak256 hash function
           Fsig = the signing algorithm
           Sig = the resulting signature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The function Fsig produces a signature (S i g) that generates the two values r and s, which are instrumental in signature verification.  S i g = r, s&lt;br&gt;
(more on this in the Ethereum book referenced below).&lt;/p&gt;

&lt;p&gt;I found this explanation by Svetlin Nakov, (PhD) to be very detailed and thus helpful: &lt;/p&gt;

&lt;p&gt;“The ECDSA signing algorithm (RFC 6979) takes as input a message msg *&lt;strong&gt;&lt;em&gt;+ a private key privKey *&lt;/em&gt;&lt;/strong&gt;and produces as output a signature, which consists of a pair of integers {r, s}. &lt;/p&gt;

&lt;p&gt;…The calculated signature {r, s} is a pair of integers, each in the range [1...n-1]. It encodes the random point R = k * G, along with a proof s, confirming that the signer knows the message h and the private key privKey. The proof s is by idea verifiable using the corresponding pubKey.”&lt;br&gt;
(See reference for more info or links ).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signature Verification&lt;/strong&gt;&lt;br&gt;
This is handled by the second part of the ECDSA algorithm. To verify a transaction one must have the signature ( r and s ), the serialized transaction and the public key. This public key must correspond to the private key used in signing the transaction initially. The signature verification algorithm takes these listed components and returns a boolean value depending on if the signature is valid or not; true for valid signature, false for invalid. The mathematical computations and the processes involved are complex and cannot be covered in this article, but below is the summary of the verification process (again, as explained in Svetlin Nakov’s book):&lt;/p&gt;

&lt;p&gt;i. Calculate the message hash, with the same cryptographic hash function used during the signing.&lt;/p&gt;

&lt;p&gt;ii. Calculate the modular inverse of the signature proof, that is the inverse of signature generation function (see function above).&lt;/p&gt;

&lt;p&gt;iii. Recover the random point, r,  generated during the signing from the x-coordinate.&lt;/p&gt;

&lt;p&gt;iv. Generate from R' its x-coordinate: r' = R'.x&lt;/p&gt;

&lt;p&gt;v. Calculate the signature validation result by comparing whether r' == r &lt;/p&gt;

&lt;p&gt;“The general idea of the signature verification is to recover the point R' using the public key and check whether it is the same point R, generated randomly during the signing process.”&lt;/p&gt;

&lt;p&gt;Again, this is a simple summary of the entire concept of signature generation and verification, by Sveltin Nakov:&lt;/p&gt;

&lt;p&gt;“The signing signing encodes a random point R (represented by its x-coordinate only) through elliptic-curve transformations using the private key privKey and the message hash h into a number s, which is the proof that the message signer knows the private key privKey. The signature {r, s} cannot reveal the private key due to the difficulty of the ECDLP problem.&lt;/p&gt;

&lt;p&gt;The signature verification decodes the proof number s from the signature back to its original point R, using the public key pubKey and the message hash h and compares the x-coordinate of the recovered R with the r value from the signature.”&lt;/p&gt;

&lt;p&gt;The signature is valid if the x-coordinate recovered, r’, is the same as the one randomly generated during the signing, r. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about v?&lt;/strong&gt;&lt;br&gt;
As already explained, in public key generation using elliptic curve cryptography, two possible public keys are generated at every instance. This is because the elliptic curve algorithm is symmetry across the x-axis, so for any value of x that is generated, there are two possible values that fit the curve; one on each side of the x-axis. &lt;br&gt;
Question now is, how does this algorithm pick the correct value of x to concatenate with the y value? This is where v comes in. &lt;/p&gt;

&lt;p&gt;According to the Ethereum Yellow paper, “ v is a 1 byte value specifying the parity and finiteness of the coordinates of the curve point for which r is the x-value.”&lt;/p&gt;

&lt;p&gt;To make it simpler, v is added to the signature verification function to help detect the correct value of r between the two possible generated values R and R’. If v is even, the R is the correct value, if v is odd, then R’. &lt;/p&gt;

&lt;p&gt;For more clarification, this is a snippet from the Ethereum book:&lt;br&gt;
“At block #2,675,000 Ethereum implemented the "Spurious Dragon" hard fork, which, among other changes, introduced a new signing scheme that includes transaction replay protection (preventing transactions meant for one network being replayed on others). This new signing scheme is specified in EIP-155. This change affects the form of the transaction and its signature, so attention must be paid to the first of the three signature variables (i.e., v), which takes one of two forms and indicates the data fields included in the transaction message being hashed.&lt;/p&gt;

&lt;p&gt;…The special signature variable v indicates two things: the chain ID and the recovery identifier to help the ECDSArecover function check the signature. It is calculated as either one of 27 or 28, or as the chain ID doubled plus 35 or 36.&lt;/p&gt;

&lt;p&gt;…The recovery identifier (27 or 28 in the "old-style" signatures, or 35 or 36 in the full Spurious Dragon–style transactions) is used to indicate the parity of the y component of the public key.”&lt;/p&gt;

&lt;p&gt;The whole idea of Spurious Dragon upgrade was to mitigate the security challenges of replay attacks and signature malleability, and this was achieved by the introduction of chain ID and recovery identifier into the variable v.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ECRECOVER&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ecrecover&lt;/strong&gt; is a global function in solidity used for signature verification. It takes the massage hash and the signature verification variables v, r, s, and returns an address. The returned address can then be compared against other addresses to find a match. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address signer = ecrecover(msgHash, v, r, s);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SECURITY ALERT:&lt;/strong&gt; The ecrecover function above is vulnerable to signature replay attack because of the reasons already explained above, so do well to avoid using it in your contracts. Openzeppelin’s ECDSA library is secure and tested for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cryptography is the backbone of blockchain technology. And a public system for value exchange and transactions such as the blockchain should be 100% secure, and should also ensure data integrity, authenticity and confidentiality.&lt;/p&gt;

&lt;p&gt;Digital signature is an efficient way to not only keep track of transaction origins, but also to ascertain that transactions are not compromised after they are signed and transmitted. The creation and verification of digital signatures are made possible by the variables v, r, s. &lt;/p&gt;

&lt;p&gt;If you find this article interesting, or perhaps cryptography, these resources are available for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://ethereum.stackexchange.com/questions/45461/verify-ecdsa-signature-in-solidity" rel="noopener noreferrer"&gt;https://ethereum.stackexchange.com/questions/45461/verify-ecdsa-signature-in-solidity&lt;/a&gt; -&amp;gt; ECDSA and signature verification implementations&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ethereum.stackexchange.com/questions/79302/why-do-we-use-serialize-function-of-ethereumjs-tx-package-before-broadcastin" rel="noopener noreferrer"&gt;https://ethereum.stackexchange.com/questions/79302/why-do-we-use-serialize-function-of-ethereumjs-tx-package-before-broadcastin&lt;/a&gt; -&amp;gt; RLP encoding and tx serialization&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ethereum.github.io/yellowpaper/paper.pdf" rel="noopener noreferrer"&gt;https://ethereum.github.io/yellowpaper/paper.pdf&lt;/a&gt;  -&amp;gt; Ethereum Yellow Paper&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ethereumbook/ethereumbook/blob/develop/06transactions.asciidoc#digital-signatures" rel="noopener noreferrer"&gt;https://github.com/ethereumbook/ethereumbook/blob/develop/06transactions.asciidoc#digital-signatures&lt;/a&gt;   -&amp;gt; Ethereum book: Digital Signatures&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages" rel="noopener noreferrer"&gt;https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages&lt;/a&gt;  -&amp;gt; Sveltin Nakov: Digital signatures&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/cryptronics/signature-replay-vulnerabilities-in-smart-contracts-3b6f7596df57" rel="noopener noreferrer"&gt;https://medium.com/cryptronics/signature-replay-vulnerabilities-in-smart-contracts-3b6f7596df57&lt;/a&gt;  -&amp;gt; Signature Vulnerabilities&lt;/p&gt;

</description>
      <category>cryptography</category>
      <category>digitalsignature</category>
      <category>ethereum</category>
      <category>smartcontract</category>
    </item>
    <item>
      <title>Ethereum as a Global State Machine.</title>
      <dc:creator>Okoli Evans</dc:creator>
      <pubDate>Sat, 28 Jan 2023 18:01:05 +0000</pubDate>
      <link>https://forem.com/okolievans/ethereum-as-a-global-state-machine-4adp</link>
      <guid>https://forem.com/okolievans/ethereum-as-a-global-state-machine-4adp</guid>
      <description>&lt;p&gt;Right from inception, the Ethereum project was clearly spelled out in a well prepared document called Ethereum whitepaper. Ethereum has always been developed, tweaked and updated to meet the goal that is the Ethereum project.&lt;/p&gt;

&lt;p&gt;What is the Ethereum project? A short summary to this question is that Ethereum was born to be a global state machine that is truly decentralized.&lt;/p&gt;

&lt;p&gt;The word global here is pretty self-explanatory... leaving us with state machine.&lt;/p&gt;

&lt;p&gt;A global state machine is a globally distributed system that achieves and maintains one outcome or result simultaneously across its entire network. State here refers to the current result at that instant. On a state machine, the outcome is always the same across the entire system or networks, or in the case of Ethereum, nodes.&lt;/p&gt;

&lt;p&gt;Now, maintaining a state on a distributed network implies that there is a mechanism to track the change in state in real time across the entire network; this also implies that the entire distributed networks when given a set of code must arrive at the same result after executing the program.&lt;/p&gt;

&lt;p&gt;So how do we track state transitions across a global network of computers? It gets tricky... to ensure that Ethereum network is deterministic, all the nodes(computers) across the world that powers the Ethereum blockchain must run an Ethereum compliant program called client.&lt;/p&gt;

&lt;p&gt;An Ethereum client must not necessary be written in a particular programming language, but it must be written according to the specifications provided in the Ethereum yellow paper (different from Ethereum white paper).&lt;br&gt;
Those programs written according to the Ethereum specifications are called Ethereum clients.&lt;/p&gt;

&lt;p&gt;Ethereum clients, when installed and ran on a computer connects that computer to the Ethereum blockchain network.&lt;br&gt;
Anybody can download any of these clients and install on their system and become an Ethereum node.&lt;/p&gt;

&lt;p&gt;Once such system successfully connects to the Ethereum blockchain network, it will download past records of the transactions recorded on the network... afterwards it will become a fullnode; and a validator if the owner meets the preset requirements.&lt;/p&gt;

&lt;p&gt;Ethereum is not just distributed, it is also decentralized. That is why it depends on independent computers across the world to run the network. No single computer has a control over what happens on the network, decisions are collectively made by the majority of all the computers on the network. The process by which this democracy is achieved on the network is overseen by an algorithm known as 'consensus mechanism'.&lt;/p&gt;

&lt;p&gt;PS: This is still pretty high level; no very technical stuff yet. Anyone can easily understand even without any prior knowledge of blockchain network. If you are intrigued by what you read, feel free to dive down the rabbit hole; simply type in 'what is blockchain technology' on Google and take it on from there. Cheerio.&lt;/p&gt;

&lt;h1&gt;
  
  
  programming #blockchain #Ethereum #ethereumblockchain
&lt;/h1&gt;

</description>
      <category>gratitude</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Err: fetching data via API works on localhost but doesn't work on live server</title>
      <dc:creator>Okoli Evans</dc:creator>
      <pubDate>Wed, 07 Sep 2022 05:34:55 +0000</pubDate>
      <link>https://forem.com/okolievans/err-fetching-data-via-api-works-on-localhost-but-doesnt-work-on-live-serve-1cji</link>
      <guid>https://forem.com/okolievans/err-fetching-data-via-api-works-on-localhost-but-doesnt-work-on-live-serve-1cji</guid>
      <description>&lt;p&gt;As a young developer, there are some minor bugs you might encounter that will be pretty confusing. Every new developer, and even experienced devs encounter this every now and then. The thing about software development is that a single comma, semicolon or colon in a wrong place will likely make your code not compile, or render an element or function undefined.&lt;/p&gt;

&lt;p&gt;When I started out newly, I once spent two days searching for a bug, only to find out that the issue was just a '}' in a wrong place... bugs are completely normal.&lt;/p&gt;

&lt;p&gt;So I was working on a demo movie app some time ago, it happened that the API fetch and every components worked perfectly on localhost, but wasn't fetching when deployed on live server. It was really confusing for me then that I had to abandon the project altogether. Recently I was going through my repositories and found the abandoned project, so I decided to try and fix it.&lt;/p&gt;

&lt;p&gt;Issue:&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;const handleSubmit = async () =&amp;gt; {&lt;br&gt;
    setLoading(true);&lt;br&gt;
    const response = await fetch(&lt;br&gt;
      `http://www.omdbapi.com/?s=${searchParam}&amp;amp;apikey=70c11897`&lt;br&gt;
    );&lt;br&gt;
    const data = await response.json();&lt;br&gt;
    console.log(data);&lt;br&gt;
    if (data) {&lt;br&gt;
      setMovieList(data.Search);&lt;br&gt;
      localStorage.setItem("movieList", JSON.stringify(data.Search));&lt;br&gt;
      setLoading(false);&lt;br&gt;
      setSearchParam("");&lt;br&gt;
    }&lt;br&gt;
  };&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;It turned out that the problem was a very simple one. The API URL I was using was '&lt;a href="http://apiurl.com/key" rel="noopener noreferrer"&gt;http://apiurl.com/key&lt;/a&gt;'. Now the problem is most web engines don't render insecure links, so my API URL was blocked from fetching data on live server. The simplest solution right? All I needed to do was add 's' to the 'http' like so '&lt;a href="https://apiurl.com/key" rel="noopener noreferrer"&gt;https://apiurl.com/key&lt;/a&gt;'.&lt;/p&gt;

&lt;p&gt;Fixed:&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;const handleSubmit = async () =&amp;gt; {&lt;br&gt;
    setLoading(true);&lt;br&gt;
    const response = await fetch(&lt;br&gt;
      `https://www.omdbapi.com/?s=${searchParam}&amp;amp;apikey=70c11897`&lt;br&gt;
    ); //notice the 's' now added to 'http'&lt;br&gt;
    const data = await response.json();&lt;br&gt;
    console.log(data);&lt;br&gt;
    if (data) {&lt;br&gt;
      setMovieList(data.Search);&lt;br&gt;
      localStorage.setItem("movieList", JSON.stringify(data.Search));&lt;br&gt;
      setLoading(false);&lt;br&gt;
      setSearchParam("");&lt;br&gt;
    }&lt;br&gt;
  };&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Like I said, some bugs can be pretty confusing for beginners, but hey, even the best developers were once beginners and coding is one skill that requires tenacity, determination and resourcefulness. So when you encounter a bug, seek for solution on open source websites or ask experienced devs for help. I hope this helps someone. ☺&lt;/p&gt;

</description>
      <category>api</category>
      <category>react</category>
      <category>javascript</category>
      <category>server</category>
    </item>
  </channel>
</rss>
