<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Okoli Evans</title>
    <description>The latest articles on DEV Community by Okoli Evans (@okolievans).</description>
    <link>https://dev.to/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>DEV Community: Okoli Evans</title>
      <link>https://dev.to/okolievans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/okolievans"/>
    <language>en</language>
    <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://dev.to/okolievans/avnu-paymaster-gas-sponsorship-on-starknet-425f</link>
      <guid>https://dev.to/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://dev.to/okolievans/from-web2-to-web3-a-practical-guide-to-building-a-gasless-crypto-payment-flow-with-starknet--1l6f</link>
      <guid>https://dev.to/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://dev.to/okolievans/understanding-digital-signatures-the-role-of-v-r-s-in-cryptographic-security-and-signature-verification-7m5</link>
      <guid>https://dev.to/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://dev.to/okolievans/ethereum-as-a-global-state-machine-4adp</link>
      <guid>https://dev.to/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://dev.to/okolievans/err-fetching-data-via-api-works-on-localhost-but-doesnt-work-on-live-serve-1cji</link>
      <guid>https://dev.to/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>
