티스토리 뷰
소셜 로그인과 zkSync native account abstraction을 활용해 Web2 사용자들을 어떻게 Web3로 온보딩할 수 있는지에 대한 빠르고 간결한 가이드.
개요
이 튜토리얼에서는 다음 주제를 다룹니다:
- 이 튜토리얼에서는 Privy와 zkSync 네트워크를 사용한 소셜 로그인을 구현합니다.
- Next.js로 프론트엔드 앱을 만들고 Vercel에 배포합니다.
- zkSync 네트워크와 상호작용하여 zkSync Sepolia 테스트넷에서 NFT를 민팅합니다.
이 튜토리얼은 이런 개발자를 대상으로 합니다:
- 원활한 소셜 로그인 경험을 제공하여 Web2 사용자들을 Web3로 온보딩하고자 하는 분
- Viem과 같은 Web3 라이브러리를 통해 zkSync 네트워크와 상호작용하고자 하는 분
- zkSync 네트워크와 상호작용하는 프로젝트를 구축하고 다음 해커톤을 준비하고자 하는 분
전체 코드
목차
- 요구 사항
- 설치
- Privy와 zkSync 네트워크를 사용한 소셜 로그인 구현
- 소셜 로그인 테스트
- 프론트엔드를 Vercel에 배포
- zkSync 네트워크와 상호작용
- 결론
- 다음 단계
- 참조
요구 사항
- Node.js (v20.10.0)
- Yarn (v1.22.21)
- zksync-cli (v1.7.1)
설치
스마트 컨트랙트
1. zksync-cli로 프로젝트 초기화
$ npx zksync-cli create --template hardhat_solidity contracts
Using Hardhat + Solidity template
? Private key of the wallet responsible for deploying contracts (optional)
? Package manager yarn
Setting up template in zkSync-native-aa-demo/contracts...
✔ Cloned template
✔ Environment variables set up
✔ Dependencies installed
🎉 All set up! 🎉
--------------------------
Navigate to your project: cd contracts
...
- 프로젝트가 Hardhat + Solidity 템플릿으로 초기화되었습니다.
- 지갑의 비공개 키는 제공하거나 나중에
.env
파일에 설정할 수 있습니다.
2. 종속성 설치
$ cd contracts && yarn add @openzeppelin/contracts@latest
- 프로젝트 생성 시
@openzeppelin/contracts
가4.6.x
버전으로 설치되었습니다. 이 튜토리얼을 작성할 시점의 최신 버전은5.0.2
이므로, 최신 보안 패치 및 업데이트를 적용하기 위해 종속성을 최신 버전으로 업데이트합니다.
프론트엔드
1. 새로운 Next.js 앱 생성
$ yarn create next-app
yarn create v1.22.21
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
$ curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
success Installed "create-next-app@14.2.3" with binaries:
- create-next-app
✔ What is your project named? … frontend
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … No
✔ Would you like to use `src/` directory? … No
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
Creating a new Next.js app in zkSync-native-aa-demo/frontend.
Using yarn.
Initializing project with template: app
...
Done in 34.30s.
2. 종속성 설치
$ cd frontend
- 프론트엔드 폴더로 디렉토리를 변경합니다.
$ yarn add @privy-io/react-auth viem @tanstack/react-query
@privy-io/react-auth
는 Privy가 지원하는 프론트엔드에서 소셜 로그인을 처리하는 라이브러리입니다.viem
은 프론트엔드에서 Web3 상호작용을 처리하는 라이브러리입니다.@tanstack/react-query
는 프론트엔드에서 데이터 가져오기 및 캐싱을 처리하는 라이브러리입니다.
$ yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
- Chakra UI 라이브러리는 프론트엔드 컴포넌트 스타일링에 사용됩니다.
- 다른 스타일링 라이브러리를 사용할 수도 있습니다. 이 튜토리얼에서는 경량화 및 사용이 쉬운 스타일링을 위해 Chakra UI를 사용합니다.
Privy
1. 새로운 Privy 앱 생성
Privy란? Google, Facebook, Twitter 등과 같은 소셜 미디어 계정으로 사용자가 로그인할 수 있게 해주는 소셜 로그인 솔루션입니다. 새로운 계정을 생성하는 불편함을 줄이고 사용자가 원활하게 로그인할 수 있도록 도와줍니다. 또한 프론트엔드에서 소셜 로그인을 처리하기 위한 간편한 훅을 제공합니다.
2. Privy 앱 ID를 환경 변수로 설정
$ cp .env.local.example .env.local
.env.local.example
파일을.env.local
로 복사합니다.- 방금 생성한 Privy 앱 ID를
.env.local
파일의NEXT_PUBLIC_PRIVY_APP_ID
변수에 설정합니다.
Privy와 zkSync 네트워크를 사용한 소셜 로그인 구현
1. providers.tsx 생성
app
디렉토리에providers.tsx
라는 새 파일을 생성합니다.
// frontend/app/providers.tsx
2. Providers 컴포넌트 생성
providers.tsx
파일에Providers
라는 새 컴포넌트를 생성합니다.
// frontend/app/providers.tsx
"use client";
import { useEffect, useState } from "react";
import { PrivyProvider } from "@privy-io/react-auth";
import { ChakraProvider } from "@chakra-ui/react";
import { zkSyncSepoliaTestnet } from "viem/zksync";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
const Providers = ({ children }: { children: React.ReactNode }) => {
const queryClient = new QueryClient();
const [mounted, setMounted] = useState<boolean>(false);
useEffect(() => {
setMounted(true);
}, []);
return (
<PrivyProvider
appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID || ""}
config={{
// zkSyncSepoliaTestnet을 사용하여 기본 체인과 지원되는 체인을 구성합니다.
defaultChain: zkSyncSepoliaTestnet,
supportedChains: [zkSyncSepoliaTestnet],
// 첫 번째 로그인 시 지갑이 없는 사용자에게 임베디드 지갑 생성
embeddedWallets: {
createOnLogin: "users-without-wallets",
},
}}
>
<ChakraProvider>
<QueryClientProvider client={queryClient}>
{mounted && children}
</QueryClientProvider>
</ChakraProvider>
</PrivyProvider>
);
};
export { Providers };
Providers
컴포넌트는PrivyProvider
,ChakraProvider
, 및QueryClientProvider
컴포넌트를 children 컴포넌트 주위에 감쌉니다.PrivyProvider
컴포넌트는 Privy를 사용한 소셜 로그인을 처리하는 데 사용됩니다.defaultChain
및supportedChains
옵션은 zkSyncSepoliaTestnet을 사용하여 기본 체인과 지원되는 체인을 구성하는 데 사용됩니다.embeddedWallets
옵션은 첫 번째 로그인 시 지갑이 없는 사용자에게 임베디드 지갑을 생성하는 데 사용됩니다.
ChakraProvider
컴포넌트는 Chakra UI를 사용한 스타일링을 처리하는 데 사용됩니다.QueryClientProvider
컴포넌트는 React Query를 사용한 데이터 가져오기 및 캐싱을 처리하는 데 사용됩니다.
3. ZkSyncClientContext 및 ZkSyncClientProvider 생성
- 동일한 파일에서
ZkSyncClientContext
라는 새로운 컨텍스트를 생성합니다. ZkSyncClientContext
컨텍스트는 지갑 및 zkSync 클라이언트를 저장하는 데 사용됩니다.ZkSyncClientProvider
컴포넌트는 지갑 및 zkSync 클라이언트를 children 컴포넌트에 제공합니다.
"use client";
import { createContext, useEffect, useState } from "react";
import {
ConnectedWallet,
PrivyProvider,
usePrivy,
useWallets,
} from "@privy-io/react-auth";
import { ChakraProvider } from "@chakra-ui/react";
import { WalletClient, createWalletClient, custom } from "viem";
import { eip712WalletActions, zkSyncSepoliaTestnet } from "viem/zksync";
import { QueryClientProvider, QueryClient } from "@tanstack/react-query";
interface ZkSyncClientContextValue {
wallet: ConnectedWallet | null;
zkSyncClient: WalletClient | null;
}
const ZkSyncClientContext = createContext({} as ZkSyncClientContextValue);
const ZkSyncClientProvider = ({ children }: { children: React.ReactNode }) => {
const { ready, authenticated } = usePrivy();
const { wallets } = useWallets();
const [wallet, setWallet] = useState<ConnectedWallet | null>(null);
const [zkSyncClient, setZkSyncClient] = useState<WalletClient | null>(null);
const zkSyncSetup = async (wallet: ConnectedWallet) => {
await wallet.switchChain(zkSyncSepoliaTestnet.id); // zkSync 체인으로 전환
const provider = await wallet.getEthereumProvider(); // EIP-1193 공급자 가져오기
const client = createWalletClient({
account: wallet.address as `0x${string}`,
chain: zkSyncSepoliaTestnet,
transport: custom(provider),
}).extend(eip712WalletActions()); // EIP-712 지갑 작업으로 클라이언트 확장
setWallet(wallet);
setZkSyncClient(client);
};
useEffect(() => {
if (ready && authenticated) {
// 임베디드 지갑 찾기
const embeddedWallet: ConnectedWallet | undefined = wallets.find(
(wallet) => wallet.walletClientType === "privy"
);
// zkSync 클라이언트 설정
if (embeddedWallet) {
zkSyncSetup(embeddedWallet);
}
}
}, [ready, authenticated, wallets]);
return (
<ZkSyncClientContext.Provider
value={{
wallet,
zkSyncClient,
}}
>
{children}
</ZkSyncClientContext.Provider>
);
};
zkSyncSetup
함수는 지갑과 함께 zkSync 클라이언트를 설정하는 데 사용됩니다.switchChain
메서드를 사용하여 네트워크를 zkSync 체인으로 전환합니다.- 지갑에서 EIP-1193 공급자를 가져오고 zkSync 클라이언트용 맞춤형 전송을 만듭니다.
- zkSync 클라이언트는 다음 단계를 위해 필요한 구조화된 데이터 서명을 위한 EIP-712 지갑 작업을 확장합니다.
const zkSyncSetup = async (wallet: ConnectedWallet) => {
await wallet.switchChain(zkSyncSepoliaTestnet.id); // zkSync 체인으로 전환
const provider = await wallet.getEthereumProvider(); // EIP-1193 공급자 가져오기
const client = createWalletClient({
account: wallet.address as `0x${string}`,
chain: zkSyncSepoliaTestnet,
transport: custom(provider),
}).extend(eip712WalletActions()); // EIP-712 지갑 작업으로 클라이언트 확장
setWallet(wallet);
setZkSyncClient(client);
};
Providers
컴포넌트를 수정하여ZkSyncClientProvider
컴포넌트를 포함합니다.
참고:
usePrivy
훅이 작동하려면PrivyProvider
컴포넌트 주위에ZkSyncClientProvider
컴포넌트가 래핑되어야 합니다.
const Providers = ({ children }: { children: React.ReactNode }) => {
...
return (
<PrivyProvider
...
>
<ChakraProvider>
<ZkSyncClientProvider>
<QueryClientProvider client={queryClient}>
{mounted && children}
</QueryClientProvider>
</ZkSyncClientProvider>
</ChakraProvider>
</PrivyProvider>
);
};
export { Providers, ZkSyncClientContext };
4. layout.tsx 수정
RootLayout
컴포넌트를 수정하여Providers
컴포넌트를 포함합니다.
// frontend/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { Providers } from "./providers";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "zkSync AA Demo",
description: "zkSync AA Demo",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Providers>{children}</Providers>
</body>
</html>
);
}
5. useZkSyncClient 훅 생성
hooks
디렉토리에useZkSyncClient
라는 새로운 훅을 생성합니다.
// frontend/hooks/useZkSyncClient.ts
import { ZkSyncClientContext } from "@/app/providers";
import { useContext } from "react";
import { createPublicClient, http } from "viem";
import { zkSyncSepoliaTestnet } from "viem/chains";
const useZkSyncClient = () => {
const publicClient = createPublicClient({
chain: zkSyncSepoliaTestnet,
transport: http(),
});
const { wallet, zkSyncClient } = useContext(ZkSyncClientContext);
return { wallet, publicClient, zkSyncClient };
};
export default useZkSyncClient;
useZkSyncClient
훅은ZkSyncClientContext
에서 지갑 및 zkSync 클라이언트를 가져오는 데 사용됩니다.- 추가로 zkSync 네트워크에서 계약을 읽기 위한 퍼블릭 클라이언트를 생성합니다.
6. Main 컴포넌트 생성
components
디렉토리에Main
이라는 새로운 컴포넌트를 생성합니다.
// frontend/components/Main.tsx
"use client";
import useZkSyncClient from "@/hooks/useZkSyncClient";
import { Box, Button, Center, Text, VStack } from "@chakra-ui/react";
import { usePrivy } from "@privy-io/react-auth";
const Main = () => {
const { ready, authenticated, login, logout } = usePrivy();
const { wallet } = useZkSyncClient();
if (!authenticated) {
return (
<Box
display="flex"
flexDirection="column"
minHeight="100vh"
bg="gray.300"
>
<Center my="auto">
<Button onClick={login} isLoading={!ready}>
Login
</Button>
</Center>
</Box>
);
}
return (
<Box display="flex" flexDirection="column" minHeight="100vh" bg="gray.300">
<Center my="auto">
<VStack
spacing={4}
direction="column"
alignItems="center"
justifyContent="center"
>
<Button onClick={logout} isLoading={!ready}>
Logout
</Button>
{wallet && <Text>Wallet address: {wallet.address}</Text>}
</VStack>
</Center>
</Box>
);
};
export default Main;
Main
컴포넌트는 사용자가 인증되지 않은 경우 로그인 버튼을 표시합니다.- 사용자가 인증된 경우 로그아웃 버튼과 지갑 주소를 표시합니다.
usePrivy
훅은 Privy를 사용한 소셜 로그인을 처리하는 데 사용됩니다.useZkSyncClient
훅은ZkSyncClientContext
에서 지갑을 가져오는 데 사용됩니다.- 사용자가 인증된 경우 임베디드 지갑 주소가 표시됩니다.
7. page.tsx 수정
Home
페이지를 수정하여 Privy로 로그인하고 사용자에게 임베디드 지갑을 생성합니다.
// frontend/app/page.tsx
import Main from "@/components/Main";
export default function Home() {
return <Main />;
}
8. 프론트엔드 실행
$ yarn dev
- 프론트엔드는
http://localhost:3000
에서 실행됩니다.
소셜 로그인 테스트
- 브라우저를 열고
http://localhost:3000
으로 이동합니다. Login
버튼을 클릭하여 Privy로 로그인합니다.- 로그인 후, 지갑 주소가 화면에 표시됩니다.
- 새 사용자는 Privy 대시보드에서 확인할 수 있습니다.
프론트엔드를 Vercel에 배포
- Vercel은 정적 사이트 및 서버리스 기능을 위한 클라우드 플랫폼입니다.
- Next.js 앱에 대한 원활한 배포 경험을 제공합니다.
- Vercel 계정에 가입하고 배포를 시작합니다.
zkSync 네트워크와 상호작용
1. ERC-721 계약 생성
hardhat.config.ts
파일을 수정하여 컴파일러 버전을0.8.24
로 변경합니다. 작성 당시 컴파일러의 최신 버전은0.8.26
이지만,0.8.25
및0.8.26
은 zkSync에서 완전히 지원되지 않으므로 호환성을 위해
0.8.24
를 사용합니다.
// contracts/hardhat.config.ts
const config: HardhatUserConfig = {
defaultNetwork: "zkSyncSepoliaTestnet",
...
solidity: {
version: "0.8.24",
},
};
export default config;
contracts
디렉토리에LibroNFT.sol
이라는 새 파일을 생성합니다.
// contracts/contracts/LibroNFT.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
/**
* @title LibroNFT
* @dev Basic ERC721 token.
*/
contract LibroNFT is ERC721 {
uint256 private _tokenId;
string private _tokenURI;
constructor(string memory uri) ERC721("LibroNFT", "LIBRO") {
_tokenURI = uri;
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
_requireOwned(tokenId);
return _tokenURI;
}
/**
* @dev Mints a new token to the sender.
*/
function mint() external {
uint256 tokenId = _tokenId++;
_safeMint(msg.sender, tokenId);
}
}
LibroNFT
계약은 기본 ERC-721 토큰 계약입니다.mint
함수는 새 토큰을 발신자에게 민팅합니다.- 모든 토큰에 대해 고정된 메타데이터 URI를 갖고 있습니다. URI는 계약 배포 시 설정됩니다.
2. ERC-721 계약 배포
- 계약을 먼저 컴파일합니다.
$ yarn hardhat compile
deploy
디렉토리에deployLibroNFT.ts
라는 새 파일을 생성합니다.
import { deployContract } from "./utils";
// 이 스크립트는 NFT 계약을 배포하고 네트워크에서 가능한 경우 블록 탐색기에 검증합니다.
export default async function () {
const tokenURI =
"https://green-main-hoverfly-930.mypinata.cloud/ipfs/QmXeQG8Kd3KT6rWaDKD9Eg2MrmRR7GG2jijgFDpcWK1Dyk";
const contract = await deployContract("LibroNFT", [tokenURI]);
}
- 배포 스크립트를 실행합니다.
$ yarn hardhat deploy-zksync --script deployLibroNFT.ts
yarn run v1.22.21
Starting deployment process of "LibroNFT"...
Estimated deployment cost: 0.242569857421466748 ETH
"LibroNFT" was successfully deployed:
- Contract address: 0x76b69a3E8D11673E547d54511831d64b81Dc9ce0
- Contract source: contracts/LibroNFT.sol:LibroNFT
- Encoded constructor arguments: 0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006268747470733a2f2f677265656e2d6d61696e2d686f766572666c792d3933302e6d7970696e6174612e636c6f75642f697066732f516d58655147384b64334b5436725761444b44394567324d726d5252374747326a696a6746447063574b3144796b000000000000000000000000000000000000000000000000000000000000
Requesting contract verification...
Your verification ID is: 14699
Contract successfully verified on zkSync block explorer!
Done in 20.29s.
LibroNFT
계약이 성공적으로 배포되었으며 zkSync 블록 탐색기에서 검증되었습니다.- zkSync 블록 탐색기에서 LibroNFT 계약 보기
3. LibroNFT.ts 생성
- 계약 ABI 및 주소를 프론트엔드에 복사합니다.
- ABI는 계약을 컴파일한 후
artifacts
디렉토리에서 또는 계약을 배포한 후deployments-zk
디렉토리에서 가져올 수 있습니다. libs
디렉토리에LibroNFT.ts
라는 새 파일을 생성합니다.
const LIBRO_NFT_ADDRESS = "0x76b69a3E8D11673E547d54511831d64b81Dc9ce0";
const LIBRO_NFT_ABI = [
...
] as const;
export { LIBRO_NFT_ADDRESS, LIBRO_NFT_ABI };
4. Main 컴포넌트 수정
Main
컴포넌트를 수정하여 다음 기능을 포함합니다:- 계정의 ETH 잔액 표시
- 계정의 NFT 잔액 표시
- 계정에 새 NFT 민팅
"use client";
import useZkSyncClient from "@/hooks/useZkSyncClient";
import { LIBRO_NFT_ABI, LIBRO_NFT_ADDRESS } from "@/libs/LibroNFT";
import { Box, Button, Center, Text, VStack } from "@chakra-ui/react";
import { usePrivy } from "@privy-io/react-auth";
import { useQuery } from "@tanstack/react-query";
import { useState } from "react";
import { formatEther } from "viem";
import { zkSyncSepoliaTestnet } from "viem/chains";
const Main = () => {
const { ready, authenticated, login, logout } = usePrivy();
const { wallet, publicClient, zkSyncClient } = useZkSyncClient();
// ====== Transaction status ======
const [txStatus, setTxStatus] = useState<string>("");
const [txHash, setTxHash] = useState<string>("");
// ====== ETH balance ======
const getEthBalance = async (): Promise<bigint> => {
if (!wallet || !publicClient) {
throw new Error("Wallet or public client not initialized");
}
const address = wallet.address as `0x${string}`;
return await publicClient.getBalance({
address,
});
};
const { data: ethBalance } = useQuery({
queryKey: ["balance", wallet?.address],
queryFn: getEthBalance,
enabled: !!wallet && !!publicClient,
refetchInterval: 3000,
});
const ethBalanceValue = formatEther(ethBalance || BigInt(0));
// ====== Token balance ======
const getTokenBalance = async (): Promise<bigint> => {
if (!wallet || !publicClient) {
throw new Error("Wallet or public client not initialized");
}
const address = wallet.address as `0x${string}`;
return await publicClient.readContract({
address: LIBRO_NFT_ADDRESS as `0x${string}`,
abi: LIBRO_NFT_ABI,
functionName: "balanceOf",
args: [address],
});
};
const { data: tokenBalance } = useQuery({
queryKey: ["tokenBalance", wallet?.address],
queryFn: getTokenBalance,
enabled: !!wallet && !!publicClient,
refetchInterval: 3000,
});
const tokenBalanceValue = (tokenBalance || BigInt(0)).toString();
// ====== Mint Libro NFT ======
const mintLibroNFT = async () => {
try {
if (!wallet || !zkSyncClient) {
throw new Error("Wallet or zkSync client not initialized");
}
const address = wallet.address as `0x${string}`;
const hash = await zkSyncClient.writeContract({
account: address,
address: LIBRO_NFT_ADDRESS as `0x${string}`,
abi: LIBRO_NFT_ABI,
functionName: "mint",
chain: zkSyncSepoliaTestnet,
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
setTxHash(hash);
setTxStatus(receipt.status);
} catch (error) {
console.error(error);
}
};
if (!authenticated) {
return (
<Box
display="flex"
flexDirection="column"
minHeight="100vh"
bg="gray.300"
>
<Center my="auto">
<Button onClick={login} isLoading={!ready}>
Login
</Button>
</Center>
</Box>
);
}
return (
<Box display="flex" flexDirection="column" minHeight="100vh" bg="gray.300">
<Center my="auto">
<VStack
spacing={4}
direction="column"
alignItems="center"
justifyContent="center"
>
<Button onClick={logout} isLoading={!ready}>
Logout
</Button>
{wallet && (
<VStack>
<Text>Address: {wallet.address}</Text>
<Text>Balance: {ethBalanceValue} ETH</Text>
<Text>Token Balance: {tokenBalanceValue}</Text>
<Button onClick={mintLibroNFT}>Mint Libro NFT</Button>
{txHash && (
<VStack>
<Text>Transaction Hash: {txHash}</Text>
<Text>Transaction Status: {txStatus}</Text>
</VStack>
)}
</VStack>
)}
</VStack>
</Center>
</Box>
);
};
export default Main;
Main
컴포넌트는 이제 ETH 잔액, NFT 잔액을 표시하고 계정에 새 NFT를 민팅할 수 있습니다.getEthBalance
함수는 계정의 ETH 잔액을 가져오는 데 사용됩니다.getTokenBalance
함수는 계정의 NFT 잔액을 가져오는 데 사용됩니다.- 두 잔액은 계약을 읽기 위한 퍼블릭 클라이언트를 사용하여 react-query를 통해 데이터 가져오기 및 캐싱합니다.
mintLibroNFT
함수는 계정에 새 NFT를 민팅하는 데 사용됩니다.- 민팅된 새 NFT의 거래 상태 및 해시가 표시됩니다.
5. 계정에 ETH 얻기
- Network Faucet
- Sepolia 테스트넷에서 다른 계정으로부터 계정에 ETH를 얻습니다.
- zkSync Bridge
- Sepolia 테스트넷에서 zkSync Sepolia로 ETH를 브리징하여 Privy가 생성한 임베디드 지갑 주소로 전송합니다.
6. NFT 민팅 테스트
결론
- 이 튜토리얼에서는 Privy와 zkSync Sepolia 테스트넷을 사용한 소셜 로그인을 구현했습니다.
- 프론트엔드는 테스트 및 데모 목적으로 Vercel에 배포되었습니다.
- 소셜 로그인 경험은 원활했으며 NFT 민팅 프로세스도 성공적이었습니다.
- zkSync 네트워크는 NFT 민팅을 위한 트랜잭션을 빠르게 처리하고 저렴한 가스비를 요구합니다.
다음 단계
- 가스비 대납을 위한 커스텀 paymaster 구현
- 멤버십 기반 가스비 대납을 구현하기 위해 필요한 사항에 대해 고민하기
참조
'블록체인 > ZKsync' 카테고리의 다른 글
EIP-7212 알아보기: secp256r1 곡선을 지원하기 위한 프리컴파일과 ZKsync에서의 구현 (0) | 2024.08.10 |
---|