What if your users could earn 3.3% APY on Bitcoin without ever knowing they're using a blockchain?
That's what we built with SmoothYield — a traditional finance mobile app (stocks, cash, portfolio) with a hidden superpower: passive BTC yield powered by Starknet. The user signs in with their email (6-digit code) or Google, taps "Stake BTC now", and starts earning. No wallet address. No gas fees.
The entire blockchain layer is built with Starkzap, the official Starknet developer toolkit. This post walks through exactly how we integrated it — step by step — so you can do the same in your own app.
What the user sees vs. what actually happens
From the user's perspective, SmoothYield looks like any fintech app:
- Sign in with email (OTP code) or Google
- View stocks (AAPL, TSLA, NVDA) and crypto prices
- Tap "BTC Yield" and stake for 3.33% APY
- See "Earning" status on the portfolio screen
What the user never sees: a wallet address, a seed phrase, a gas fee prompt, or the word "Starknet".
Behind the scenes, a lot is happening:
User taps "Stake BTC now"
│
▼
Backend creates a Starknet wallet (Privy)
│
▼
Backend onboards wallet into Starkzap
│
▼
Starkzap finds an LBTC staking pool
│
▼
Starkzap builds the staking transaction
│
▼
Privy signs it server-side (no user interaction)
│
▼
AVNU paymaster pays the gas (user pays nothing)
│
▼
Transaction lands on Starknet
│
▼
App shows "Earning 3.33% APY"
All of that is about 200 lines of backend TypeScript across two files. Let me show you.
The stack
Four npm packages handle the entire crypto layer:
| Package | What it does |
|---|---|
starkzap |
Starknet SDK — wallet onboarding, staking, token presets, paymaster |
@privy-io/node |
Server-side embedded wallet creation + signing |
@privy-io/expo |
Client-side auth (email OTP + Google OAuth) |
starknet |
Low-level Starknet RPC provider |
The app itself is:
- Mobile: Expo React Native (tabs for Stocks, Crypto, Portfolio, Profile)
- Backend: Express + SQLite (auth, wallet management, staking endpoints). For production, swap SQLite for PostgreSQL with encrypted columns and session expiry.
Step 1: Initialize Starkzap with the paymaster
The paymaster is the key to the "no gas fees" experience. You get an API key from portal.avnu.fi, and every transaction your app sends is free for the user.
const starkzap = await import("starkzap");
const sdk = new starkzap.StarkZap({
network: "mainnet",
rpcUrl: "https://starknet-mainnet.g.alchemy.com/v2/YOUR_KEY",
// This makes every transaction gasless
paymaster: {
nodeUrl: "https://starknet.paymaster.avnu.fi",
headers: { "x-paymaster-api-key": process.env.AVNU_PAYMASTER_API_KEY },
},
});
That's it. Every wallet.execute() call from this point forward is sponsored. The user never needs to hold ETH or STRK.
Docs: Starkzap Paymasters and AVNU integration
Step 2: Create invisible wallets with Privy
When a user signs in (email OTP or Google), the backend creates a Starknet wallet for them. One API call:
import { PrivyClient } from "@privy-io/node";
const privy = new PrivyClient({
appId: process.env.PRIVY_APP_ID,
appSecret: process.env.PRIVY_APP_SECRET,
});
const wallet = await privy.wallets().create({
chain_type: "starknet",
});
// wallet.id → used for signing later
// wallet.address → Starknet address (stored in DB, never shown to user)
// wallet.public_key → needed for Starkzap onboard
The wallet is "server-managed" — the backend holds it, the mobile app never touches it. This means signing happens entirely on the server, which is exactly what we want for a TradFi experience.
Step 3: Onboard the wallet into Starkzap
This is where Privy and Starkzap meet. The sdk.onboard() call connects the Privy wallet to the Starkzap SDK so you can execute staking transactions through it:
const wallet = await sdk.onboard({
strategy: "privy",
privy: {
resolve: async () => ({
walletId: privyWalletId,
publicKey: publicKey,
serverUrl: "https://your-api.com/api/wallet/sign",
}),
},
deploy: "never",
feeMode: "sponsored",
});
Two things to note:
serverUrl — Starkzap will call this endpoint whenever it needs a signature. Your backend handles it with Privy's rawSign:
app.post("/api/wallet/sign", async (req, res) => {
const { walletId, hash } = req.body;
const result = await privy.wallets().rawSign(walletId, {
params: { hash },
});
res.json({ signature: result.signature });
});
deploy: "never" — we skip deployment during the onboard step. Instead, the backend deploys the account explicitly via deployAccountIfNeeded() when the user initiates their first action (the /wallet/init call). This keeps onboarding fast and the deploy sponsored by the paymaster. Reading token balances works even before deployment since balance_of is a read-only call on the token contract.
Step 4: Stake LBTC for yield
This is the payoff. With the wallet onboarded, staking LBTC is a handful of lines:
const { Staking, Amount, ChainId, getStakingPreset } = await import("starkzap");
// Get the staking config for this network
const stakingConfig = getStakingPreset(ChainId.MAINNET);
// Connect to a validator's LBTC pool
const staking = await Staking.fromPool(poolAddress, provider, stakingConfig);
// Build the staking transaction
const amount = Amount.fromRaw("100000000", 8, "LBTC"); // 1 LBTC
const isMember = await staking.isMember(wallet);
const calls = isMember
? staking.populateAdd(walletAddress, amount) // add to existing stake
: staking.populateEnter(walletAddress, amount); // first-time stake
// Execute — gasless, server-signed, on-chain
const tx = await wallet.execute(calls, { feeMode: "sponsored" });
How does the backend know which pool to use? Starkzap ships a validator registry. You iterate it once to find an LBTC pool:
const validators = starkzap.mainnetValidators;
for (const validator of Object.values(validators)) {
const pools = await Staking.getStakerPools(
provider,
validator.stakerAddress,
stakingConfig,
);
const lbtcPool = pools.find((p) => p.token.symbol === "LBTC");
if (lbtcPool) {
return lbtcPool.poolContract; // cache this
}
}
We cache the pool address for the lifetime of the process. No need to look it up on every request.
Step 5: Unstake (when the user wants out)
Starknet staking has a two-phase exit. Starkzap handles both:
// Phase 1: "I want to withdraw" (exit intent)
const exitIntentCall = staking.populateExitIntent(amount);
await wallet.execute([exitIntentCall], { feeMode: "sponsored" });
// Phase 2: After cooldown passes, finalize
const exitCall = staking.populateExit(walletAddress);
await wallet.execute([exitCall], { feeMode: "sponsored" });
Both calls are gasless. The user just taps a button.
How the mobile app ties it together
On the frontend, the user has no idea any of this exists. The mobile app uses Privy for auth (email OTP or Google) and talks to the backend with a simple session ID:
// app/_layout.tsx — wrap the app in PrivyProvider
<PrivyProvider appId={appId}>
<AuthProvider>
<App />
</AuthProvider>
</PrivyProvider>
The AuthProvider exchanges the Privy token for a backend session:
const privyToken = await getAccessToken();
const { sessionId } = await api.post("/api/auth/session", { privyToken });
From then on, every API call uses x-session-id as a header. The staking flow is two REST calls:
// 1. Initialize wallet (creates + deploys if needed)
await api.post("/api/wallet/init");
// 2. Stake
const result = await api.post("/api/yield/stake", {
amountLbtc: "100000000",
});
// result.txHash → "0x04a3..."
// result.explorerUrl → "https://voyager.online/tx/0x04a3..."
The YieldScreen component shows a simple input for the amount, a "Stake BTC now" button, and an "Earning X% APY" card after success. Standard React Native — nothing crypto-specific in the UI layer.
The three API keys you need
| Service | Free tier | Get it at |
|---|---|---|
| Privy | Yes | dashboard.privy.io |
| AVNU | Yes | portal.avnu.fi |
| Alchemy (optional) | Yes | dashboard.alchemy.com |
Set them in your backend .env:
PRIVY_APP_ID=your-privy-app-id
PRIVY_APP_SECRET=your-privy-app-secret
AVNU_PAYMASTER_API_KEY=your-avnu-key
ALCHEMY_STARKNET_API_KEY=your-alchemy-key
STARKNET_NETWORK=mainnet
Why this matters if you're a web2 developer
You already have the skills. If you can build an Express API, call a REST endpoint, and wire up React Native screens, you can ship a crypto-powered feature. Starkzap works like any other npm SDK — install it, call functions, get results. The staking flow is Staking.fromPool() → populateEnter() → wallet.execute(). No Solidity, no ABIs, no local nodes.
Your users never deal with gas. The AVNU paymaster sponsors every transaction. You add an API key, pass { feeMode: "sponsored" }, and gas is invisible. Think of it like Stripe absorbing the payment processing fee — your user just sees the result.
Auth stays familiar. Privy handles wallets the same way you'd handle any server-side resource. The user signs in with their email and a 6-digit code. No MetaMask, no browser extensions, no "approve this transaction" popups. The wallet lives on your backend, managed through a simple API. And it's not a lock-in: Starknet is a Privy Tier 2 chain, so users can export their private keys at any time if they want to self-custody.
No blockchain knowledge required. You don't need to write Cairo or understand Starknet internals. Starkzap's token presets give you the right contract addresses, the Staking module builds the transactions, and the paymaster handles the fees. The blockchain is just an infrastructure layer — the same way you use PostgreSQL without understanding B-trees.
The ramp-up is minimal. The only new concepts are three API keys (Privy, AVNU, Alchemy) and how Privy tokens flow between client and server. Once that's wired up, adding new on-chain features is just calling more Starkzap functions. If you can build a Node.js backend, you can build this.
Try it yourself
The full source code is on GitHub: smoothyield
git clone https://github.com/starkience/smoothyield
cd smoothyield/backend
cp .env.example .env # add your API keys
npm install && npm run dev
cd ../mobile
cp .env.example .env
npm install && npm start
Set DEV_MODE=true in the backend .env to test the full flow with mocked transactions before you add real API keys.
Resources
- Starkzap documentation
- Starkzap + Privy integration guide
- Starkzap Paymasters
- AVNU Paymaster integration
- AVNU Portal (get your paymaster key)
- Privy Dashboard
If you're building a fintech app and want to add crypto yield without turning it into a "crypto app", Starkzap + Privy + AVNU paymaster is the stack. Four packages. Two hundred lines. Zero blockchain UX for your users.
Top comments (0)