Step 6 - Set Up the /api Folder + Magmar Routes

In this step we wire up every server-side call your dApp needs. You’ll create four HTTP endpoints in /app/api that rely on the Magmar SDK stack.

Endpoint
Purpose
Magmar module

/api/get-signer

Deterministically derive the owner address behind a Smart-Contract Wallet (SCW)

@magmar/aa-core

/api/get-user-nfts

Fetch NFTs owned by an SCW for dashboard display

magmar-nft-sdk

/api/mint-nft-user-op

Send a sponsored UserOperation that mints an NFT to the user’s SCW

@magmar/aa-core + @magmar/aa-paymaster

/api/get-user

Retrieve a user’s encrypted private key from Userbase (demo only)

axios

6.1 Install Magmar Server-Side Dependencies

npm i @magmar/aa-core @magmar/aa-paymaster magmar-nft-sdk axios

6.2 Project Structure

/app
  /api
    /get-signer
      route.ts
    /get-user-nfts
      route.ts
    /mint-nft-user-op
      route.ts
    /get-user
      route.ts

6.3 /get-signer/route.ts

import { withMagmarPaymaster } from "@magmar/aa-paymaster";
import {
  LocalAccountSigner,
  SimpleSmartContractAccount,
  SmartAccountProvider,
  type SimpleSmartAccountOwner,
} from "@magmar/aa-core";

import { NextRequest, NextResponse } from "next/server";
import { sepolia } from "viem/chains";

const RPC_URL = process.env.SEPOLIA_RPC_URL!;
const ENTRYPOINT = process.env.SEPOLIA_ENTRYPOINT_ADDRESS as `0x${string}`;
const FACTORY   = process.env.SEPOLIA_SIMPLE_ACCOUNT_FACTORY_ADDRESS as `0x${string}`;
const POLICY_ID = process.env.SEPOLIA_PAYMASTER_POLICY_ID!;          // created in Step 2

export async function POST(req: NextRequest) {
  const { pk } = await req.json();                        // raw private-key hex
  const owner: SimpleSmartAccountOwner =
    LocalAccountSigner.privateKeyToAccountSigner(`0x${pk}`);

  const provider = new SmartAccountProvider(RPC_URL, ENTRYPOINT, sepolia);
  let signer = provider.connect(
    (rpc) =>
      new SimpleSmartContractAccount({
        entryPointAddress: ENTRYPOINT,
        chain: sepolia,
        owner,
        factoryAddress: FACTORY,
        rpcClient: rpc,
      }),
  );

  /* Attach Magmar paymaster for sponsored gas */
  signer = withMagmarPaymaster(signer, { policyId: POLICY_ID });

  const ownerAddress = (signer.account as any).owner.owner.address;
  return NextResponse.json({ data: ownerAddress });
}

6.4 /get-user-nfts/route.ts

import { NextRequest, NextResponse } from "next/server";
import { MagmarNFT } from "magmar-nft-sdk";

const nft = new MagmarNFT({ rpcUrl: process.env.SEPOLIA_RPC_URL! });

export async function POST(req: NextRequest) {
  const { address } = await req.json();
  const nfts = await nft.getNftsForOwner(address);
  return NextResponse.json({ data: nfts });
}

6.5 /mint-nft-user-op/route.ts (sponsored mint)

import { withMagmarPaymaster } from "@magmar/aa-paymaster";
import {
  LocalAccountSigner,
  SimpleSmartContractAccount,
  SmartAccountProvider,
  SendUserOperationResult,
  type SimpleSmartAccountOwner,
} from "@magmar/aa-core";

import nftAbi from "@common/utils/NFTContract.json";
import axios       from "axios";
import { encodeFunctionData, parseEther } from "viem";
import { sepolia } from "viem/chains";
import { NextRequest, NextResponse } from "next/server";

const RPC_URL     = process.env.SEPOLIA_RPC_URL!;
const ENTRYPOINT  = process.env.SEPOLIA_ENTRYPOINT_ADDRESS  as `0x${string}`;
const FACTORY     = process.env.SEPOLIA_SIMPLE_ACCOUNT_FACTORY_ADDRESS as `0x${string}`;
const POLICY_ID   = process.env.SEPOLIA_PAYMASTER_POLICY_ID!;
const NFT_ADDR    = process.env.SEPOLIA_NFT_ADDRESS as `0x${string}`;

export async function POST(req: NextRequest) {
  const { userId, userScwAddress } = await req.json();

  /* 1 – Load demo private key from Userbase */
  const { data } = await axios.get(
    `https://v1.userbase.com/v1/admin/users/${userId}`,
    { headers: { Authorization: `Bearer ${process.env.USERBASE_ACCESS_TOKEN}` } },
  );
  const pk = data?.profile?.pk as string;

  /* 2 – Create signer */
  const signer = await buildSigner(pk);

  /* 3 – Encode `mint(address)` call */
  const callData = encodeFunctionData({
    abi: nftAbi,
    functionName: "mint",
    args: [userScwAddress],
  });

  /* 4 – Send sponsored UserOperation */
  const result: SendUserOperationResult = await signer.sendUserOperation({
    target: NFT_ADDR,
    data:   callData,
    value:  parseEther("0"),
  });

  /* 5 – Wait for inclusion */
  const txHash   = await signer.waitForUserOperationTransaction(result.hash);
  const receipt  = await signer.rpcClient.waitForTransactionReceipt({ hash: txHash });

  return NextResponse.json({ receipt });
}

async function buildSigner(rawPk: string) {
  const owner: SimpleSmartAccountOwner =
    LocalAccountSigner.privateKeyToAccountSigner(`0x${rawPk}`);

  const provider = new SmartAccountProvider(RPC_URL, ENTRYPOINT, sepolia);
  let signer = provider.connect(
    (rpc) =>
      new SimpleSmartContractAccount({
        entryPointAddress: ENTRYPOINT,
        chain: sepolia,
        owner,
        factoryAddress: FACTORY,
        rpcClient: rpc,
      }),
  );

  return withMagmarPaymaster(signer, { policyId: POLICY_ID });
}

6.6 /get-user/route.ts (demo-only key fetch)

import axios from "axios";
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const { userId } = await req.json();
  const { data }  = await axios.get(
    `https://v1.userbase.com/v1/admin/users/${userId}`,
    { headers: { Authorization: `Bearer ${process.env.USERBASE_ACCESS_TOKEN}` } },
  );
  return NextResponse.json({ response: data });
}

6.7 Update .env.local

# Chain & RPC
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/your-key

# Magmar AA
SEPOLIA_ENTRYPOINT_ADDRESS=0x...
SEPOLIA_SIMPLE_ACCOUNT_FACTORY_ADDRESS=0x...
SEPOLIA_NFT_ADDRESS=0x5700D74F864CE224fC5D39a715A744f8d1964429
SEPOLIA_PAYMASTER_POLICY_ID=your-magmar-policy-id

# Userbase
USERBASE_ACCESS_TOKEN=your-userbase-token

Result

  • /sign-up and /login now compile without red errors

  • A new user can register, mint an NFT, and view it all gas-free using Magmar Paymaster sponsorship

Last updated