From 54c56ad32bb9a36cbca2c54da3a2eeeb47aa3c16 Mon Sep 17 00:00:00 2001 From: Rahat Date: Tue, 15 Aug 2023 08:11:01 -0400 Subject: [PATCH] add next js tutorial --- docs/tutorials/React_vite/first-dapp.md | 4 +- docs/tutorials/nextjs/_category_.json | 8 + docs/tutorials/nextjs/dashboard.md | 21 + docs/tutorials/nextjs/gaslesstransaction.md | 505 ++++++++++++++++++++ docs/tutorials/nextjs/initialize.md | 305 ++++++++++++ docs/tutorials/nextjs/introduction.md | 14 + docs/tutorials/nextjs/nft.md | 37 ++ docs/tutorials/nextjs/sdkintegration.md | 181 +++++++ 8 files changed, 1073 insertions(+), 2 deletions(-) create mode 100644 docs/tutorials/nextjs/_category_.json create mode 100644 docs/tutorials/nextjs/dashboard.md create mode 100644 docs/tutorials/nextjs/gaslesstransaction.md create mode 100644 docs/tutorials/nextjs/initialize.md create mode 100644 docs/tutorials/nextjs/introduction.md create mode 100644 docs/tutorials/nextjs/nft.md create mode 100644 docs/tutorials/nextjs/sdkintegration.md diff --git a/docs/tutorials/React_vite/first-dapp.md b/docs/tutorials/React_vite/first-dapp.md index 5dafb895..1fdc9315 100644 --- a/docs/tutorials/React_vite/first-dapp.md +++ b/docs/tutorials/React_vite/first-dapp.md @@ -1,9 +1,9 @@ --- -sidebar_label: 'Build your first AA powered dApp' +sidebar_label: 'Introduction' sidebar_position: 1 --- -# Build your first AA powered dApp +# Introduction This tutorial will cover the basics of creating a dApp powered by the Biconomy SDK. Here's an overview of what you'll learn: diff --git a/docs/tutorials/nextjs/_category_.json b/docs/tutorials/nextjs/_category_.json new file mode 100644 index 00000000..6b422d1e --- /dev/null +++ b/docs/tutorials/nextjs/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Next JS Fullstack Tutorial", + "position": 3, + "link": { + "type": "generated-index", + "description": "An NFT Minting dApp designed for web2 users to onboard to web3 with no wallet and no funds for gas." + } +} diff --git a/docs/tutorials/nextjs/dashboard.md b/docs/tutorials/nextjs/dashboard.md new file mode 100644 index 00000000..f60ea3dc --- /dev/null +++ b/docs/tutorials/nextjs/dashboard.md @@ -0,0 +1,21 @@ +--- +sidebar_position: 3 +--- + +# Paymaster, Bundler, And The Biconomy Dashboard + +Now, let's setup our biconomy dashboard. Follow the instructions [on this page](https://docs.biconomy.io/docs/category/biconomy-dashboard). + +Before continuing onto the the next step be sure that you have completed the following: + +- Created an account on the Biconomy Dashboard. +- Register a new paymaster on the Dashboard. +- Get the [paymaster URL](https://docs.biconomy.io/docs/dashboard/keys) and bundler URL from dashboard. +- Set up a Gas tank with some Base Goerli Eth. We recommend around .02 Eth to start. +- Whitelist the `safeMint` method of the NFT smart contract under [Paymaster Policies](https://docs.biconomy.io/docs/dashboard/paymasterPolicies) on the dashboard. + +:::info +Missing any of the above steps causes a lot of [common errors](docs/troubleshooting/commonerrors.md) to occur. This is a good checklist to have when building new dApps utilizing the Biconomy SDK. +::: + +Now with our smart contracts ready to go along with our Paymaster and Bundler URLs let's get started with our frontend. \ No newline at end of file diff --git a/docs/tutorials/nextjs/gaslesstransaction.md b/docs/tutorials/nextjs/gaslesstransaction.md new file mode 100644 index 00000000..ae0e887c --- /dev/null +++ b/docs/tutorials/nextjs/gaslesstransaction.md @@ -0,0 +1,505 @@ +--- +sidebar_position: 6 +--- + +# Execute a Gasless Transaction + +In the previous section our work has primarily been in the `index.tsx` file. Let's now create a new component that will handle all of our mint logic. + +In your `src` directory create a new folder called `components` and within the folder create a `Minter.tsx` file. + +Doing any transaction with a smart contract requires the ABI of that contract. As a reminder if you are using the already deployed contract you can get the ABI directly from [here on basescan](https://goerli.basescan.org/token/0x0a7755bdfb86109d9d403005741b415765eaf1bc#code). + +In your `src` firectory create a folder named `utils` and create a file called `abi.json`. Copy the abi into that folder. + +Let's get started with building our component, first the imports: + +```typescript + +import { useState } from 'react'; +import { ethers } from "ethers"; +import abi from "../utils/abi.json" +import { + IHybridPaymaster, + SponsorUserOperationDto, + PaymasterMode +} from '@biconomy/paymaster' +import { BiconomySmartAccount } from "@biconomy/account" +import styles from '@/styles/Home.module.css' + +``` + +These are all of the imports you need to execute the gasless transaction. + +I also added the NFT address below the imports as a variable: + +```typescript +const nftAddress = "0x0a7755bDfb86109D9D403005741b415765EAf1Bc" +``` + +If you deployed your own make sure to replace the address. + +For type safety we're going to create an interface for our the Props of our component: + +```typescript + +interface Props { + smartAccount: BiconomySmartAccount, + address: string, + provider: ethers.providers.Provider, +} + +``` + +With our interface created lets start scaffolding out our component: + +```typescript + +const Minter: React.FC = ({ smartAccount, address, provider }) => { + return( + < + {address && }> + + ) +} + +``` +We're going to need to pass three items to this component: the instance of the smartAccount we saved to state in the index, the address of the smart account, as well as the provider from Particle Auth for signing the transactions before executing them. I have also added a Mint NFT button that needs a handleMint funciton. Let's write that function now: + +```typescript + + const handleMint = async () => { + const contract = new ethers.Contract( + nftAddress, + abi, + provider, + ) + try { + const minTx = await contract.populateTransaction.safeMint(address); + console.log(minTx.data); + const tx1 = { + to: nftAddress, + data: minTx.data, + }; + let userOp = await smartAccount.buildUserOp([tx1]); + console.log({ userOp }) + const biconomyPaymaster = + smartAccount.paymaster as IHybridPaymaster; + let paymasterServiceData: SponsorUserOperationDto = { + mode: PaymasterMode.SPONSORED, + }; + const paymasterAndDataResponse = + await biconomyPaymaster.getPaymasterAndData( + userOp, + paymasterServiceData + ); + + userOp.paymasterAndData = paymasterAndDataResponse.paymasterAndData; + const userOpResponse = await smartAccount.sendUserOp(userOp); + console.log("userOpHash", userOpResponse); + const { receipt } = await userOpResponse.wait(1); + console.log("txHash", receipt.transactionHash); + } catch (err: any) { + console.error(err); + console.log(err) + } + } + +``` + +Here is what the above code does: + +- we connect to the contract using ethers +- we use the ethers `populateTransaction` method in order to create a raw transaction object +- we start constructing our transaction which is simply the start of our userOperation object: + +```typescript + const tx1 = { + to: nftAddress, + data: minTx.data, + }; +``` + +The to value is what contract we are interacting with and the data field takes the data from our raw transaction object. + +- We now use built in smartAccount methods to begin building the userOperation object. +```typescript + let userOp = await smartAccount.buildUserOp([tx1]); +``` +The next few lines are important in making sure this becomes a gasless transaciton. We need to update the userOp to also include the `paymasterAndData` field so when the entry point contract executes the transaction, our gas tank on our paymaster will pay for the transaction cost. + +```typescript + +let paymasterServiceData: SponsorUserOperationDto = { + mode: PaymasterMode.SPONSORED, + }; + const paymasterAndDataResponse = + await biconomyPaymaster.getPaymasterAndData( + userOp, + paymasterServiceData + ); + +``` + +In the background the sdk is making a call to our Paymaster API (which is something you can actually interact with yourself!) and returning the data we need for this operation. + +Finally we add the data to the userOp and send the userOp! + +```typescript + userOp.paymasterAndData = paymasterAndDataResponse.paymasterAndData; + const userOpResponse = await smartAccount.sendUserOp(userOp); + const { receipt } = await userOpResponse.wait(1); +``` +The `wait` function optionally takes a number here if you want to wait for a specific number of network confirmations before considering this a success. In this case I just passed the number 1 in order to make sure there was at least 1 confirmation before showing the user any success messages. + +Let's add two more things here for a better user experience: + +First an additional state variable that keeps track of user having minted an NFT within this session: + +```typescript +const [minted, setMinted] = useState(false) +``` + +By default it will be set to false and after our userOpResponse has completed we can update the state: + +```typescript +const { receipt } = await userOpResponse.wait(1); +setMinted(true) +``` + +Let's update the JSX: + +```typescript +<> + {minted && Click to view minted nfts for smart account} + + +``` +Now after succesfully minting we'll show a link users can click to view their NFT on opensea. + +We'll also add in React Toastify to send updates to the user regarding the transaction. + +```bash +yarn add react-toastify +``` + +We'll add these imports: + +```typescript +import { toast, ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +``` + +And make another update to the `handleMint` function: + +```typescript + const handleMint = async () => { + const contract = new ethers.Contract( + nftAddress, + abi.abi, + provider, + ) + try { + toast.info('Minting your NFT...', { + position: "top-right", + autoClose: 15000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + const minTx = await contract.populateTransaction.safeMint(address); + console.log(minTx.data); + const tx1 = { + to: nftAddress, + data: minTx.data, + }; + console.log("here before userop") + let userOp = await smartAccount.buildUserOp([tx1]); + console.log({ userOp }) + const biconomyPaymaster = + smartAccount.paymaster as IHybridPaymaster; + let paymasterServiceData: SponsorUserOperationDto = { + mode: PaymasterMode.SPONSORED, + }; + const paymasterAndDataResponse = + await biconomyPaymaster.getPaymasterAndData( + userOp, + paymasterServiceData + ); + + userOp.paymasterAndData = paymasterAndDataResponse.paymasterAndData; + const userOpResponse = await smartAccount.sendUserOp(userOp); + console.log("userOpHash", userOpResponse); + const { receipt } = await userOpResponse.wait(1); + console.log("txHash", receipt.transactionHash); + setMinted(true) + toast.success(`Success! Here is your transaction:${receipt.transactionHash} `, { + position: "top-right", + autoClose: 18000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + } catch (err: any) { + console.error(err); + console.log(err) + } + } +``` + +To make sure that this toast shows up in our dApp we need to add the component into the JSX, here is the last update: + +```typescript + <> + {address && } + {minted && Click to view minted nfts for smart account} + + +``` + +Don't forget to also import the Minter function into your `index.tsx` + +```typescript +
+

Based Account Abstraction

+

Connect and Mint your AA powered NFT now

+ {!loading && !address && } + {loading &&

Loading Smart Account...

} + {address &&

Smart Account: {address}

} + {smartAccount && provider && } +
+``` + +Now you're all set, you created a Next JS application that leverages Account Abstraction and Social Logins via the Biconomy SDK and Particle Auth. If you need to review the completed code check out the full snippets below for the `index.tsx` and `Minter.tsx` files! + +
+ index.tsx + + ```typescript + import Head from 'next/head' +import { + ParticleAuthModule, + ParticleProvider, +} from "@biconomy/particle-auth"; +import styles from '@/styles/Home.module.css' +import { useState } from 'react'; +import { IBundler, Bundler } from '@biconomy/bundler' +import { BiconomySmartAccount, BiconomySmartAccountConfig, DEFAULT_ENTRYPOINT_ADDRESS } from "@biconomy/account" +import { ethers } from 'ethers' +import { ChainId } from "@biconomy/core-types" +import { + IPaymaster, + BiconomyPaymaster, +} from '@biconomy/paymaster' +import Minter from '@/components/Minter'; + + + +export default function Home() { + const [address, setAddress] = useState("") + const [loading, setLoading] = useState(false); + const [smartAccount, setSmartAccount] = useState(null); + const [provider, setProvider] = useState(null) + + const particle = new ParticleAuthModule.ParticleNetwork({ + projectId: "bb8d58f8-0d3c-4306-a5f1-6cc7aa73b012", + clientKey: "c9rwyb2a3pQhHapL1EphoNKYnFsVQkAEHgWP5TRm", + appId: "bd23aa64-ef27-4054-a823-25aa32d903a4", + wallet: { + displayWalletEntry: true, + defaultWalletEntryPosition: ParticleAuthModule.WalletEntryPosition.BR, + }, + }); + + const bundler: IBundler = new Bundler({ + bundlerUrl: 'https://bundler.biconomy.io/api/v2/84531/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44', + chainId: ChainId.BASE_GOERLI_TESTNET, + entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + }) + + + + const paymaster: IPaymaster = new BiconomyPaymaster({ + paymasterUrl: 'https://paymaster.biconomy.io/api/v1/84531/m814QNmpW.fce62d8f-41a1-42d8-9f0d-2c65c10abe9a' + }) + + const connect = async () => { + try { + setLoading(true) + const userInfo = await particle.auth.login(); + console.log("Logged in user:", userInfo); + const particleProvider = new ParticleProvider(particle.auth); + console.log({particleProvider}) + const web3Provider = new ethers.providers.Web3Provider( + particleProvider, + "any" + ); + setProvider(web3Provider) + const biconomySmartAccountConfig: BiconomySmartAccountConfig = { + signer: web3Provider.getSigner(), + chainId: ChainId.BASE_GOERLI_TESTNET, + bundler: bundler, + paymaster: paymaster + } + let biconomySmartAccount = new BiconomySmartAccount(biconomySmartAccountConfig) + biconomySmartAccount = await biconomySmartAccount.init() + setAddress( await biconomySmartAccount.getSmartAccountAddress()) + setSmartAccount(biconomySmartAccount) + setLoading(false) + } catch (error) { + console.error(error); + } + }; + + return ( + <> + + Based Account Abstraction + + +
+

Based Account Abstraction

+

Connect and Mint your AA powered NFT now

+ {!loading && !address && } + {loading &&

Loading Smart Account...

} + {address &&

Smart Account: {address}

} + {smartAccount && provider && } +
+ + ) +} + + ``` +
+ +
+ Minter.tsx + + ```typescript + + import { useState } from 'react'; +import { ethers } from "ethers"; +import abi from "../utils/abi.json" +import { + IHybridPaymaster, + SponsorUserOperationDto, + PaymasterMode +} from '@biconomy/paymaster' +import { BiconomySmartAccount } from "@biconomy/account" +import { toast, ToastContainer } from 'react-toastify'; +import styles from '@/styles/Home.module.css' +import 'react-toastify/dist/ReactToastify.css'; + +const nftAddress = "0x0a7755bDfb86109D9D403005741b415765EAf1Bc" + +interface Props { + smartAccount: BiconomySmartAccount, + address: string, + provider: ethers.providers.Provider, +} + +const Minter: React.FC = ({ smartAccount, address, provider }) => { + const [minted, setMinted] = useState(false) + + const handleMint = async () => { + const contract = new ethers.Contract( + nftAddress, + abi.abi, + provider, + ) + try { + toast.info('Minting your NFT...', { + position: "top-right", + autoClose: 15000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + const minTx = await contract.populateTransaction.safeMint(address); + console.log(minTx.data); + const tx1 = { + to: nftAddress, + data: minTx.data, + }; + console.log("here before userop") + let userOp = await smartAccount.buildUserOp([tx1]); + console.log({ userOp }) + const biconomyPaymaster = + smartAccount.paymaster as IHybridPaymaster; + let paymasterServiceData: SponsorUserOperationDto = { + mode: PaymasterMode.SPONSORED, + }; + const paymasterAndDataResponse = + await biconomyPaymaster.getPaymasterAndData( + userOp, + paymasterServiceData + ); + + userOp.paymasterAndData = paymasterAndDataResponse.paymasterAndData; + const userOpResponse = await smartAccount.sendUserOp(userOp); + console.log("userOpHash", userOpResponse); + const { receipt } = await userOpResponse.wait(1); + console.log("txHash", receipt.transactionHash); + setMinted(true) + toast.success(`Success! Here is your transaction:${receipt.transactionHash} `, { + position: "top-right", + autoClose: 18000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + } catch (err: any) { + console.error(err); + console.log(err) + } + } + return( + <> + {address && } + {minted && Click to view minted nfts for smart account} + + + ) +} + +export default Minter; + + + ``` + + +
\ No newline at end of file diff --git a/docs/tutorials/nextjs/initialize.md b/docs/tutorials/nextjs/initialize.md new file mode 100644 index 00000000..1adc6661 --- /dev/null +++ b/docs/tutorials/nextjs/initialize.md @@ -0,0 +1,305 @@ +--- +sidebar_position: 4 +--- + +# Initialize Frontend + +Now it's time to work on our Frontend. We will be using Next JS which is a popular react Framework used by many Web3 projects for frontends. + +In your command prompt tool of choice navigate to any directory of choice and create your Next JS application using the [Create Next App tool](https://nextjs.org/docs/pages/api-reference/create-next-app). + +With NPM: + +```bash +npx create-next-app@latest +``` + +With Yarn: + +```bash +yarn create next-app +``` +This is how we answered the Propts when creating the application: + +```bash +What is your project named? based-aa +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? Yes +Would you like to use App Router? (recommended) No +Would you like to customize the default import alias? Yes +``` + +:::info +Although the App router is mentioned as the preferred way of using Next, the pages router is much more stable at the time of writing this tutorial. We recommend saying no when promoted to use the App router. +::: + +You can follow the prompts to receive the scaffolding for your React application. You can use any of the package managers of your choice, but to keep things simple we will be using Yarn from this point. + +Install the following dependencies: + +```bash +yarn add @biconomy/account @biconomy/bundler @biconomy/common @biconomy/core-types @biconomy/paymaster @biconomy/particle-auth ethers@5.7.2 +``` +Additionaly to help with some polyfill errors we will need to update our `next.config.js` file which is located at the root of our project. Copy the code below and replace the current contents of the file: + +```js +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + webpack: (config, { isServer }) => { + if (!isServer) { + config.resolve.fallback = { + "fs": false, + "net": false, + "tls": false + } + } + return config + } +} + +module.exports = nextConfig +``` + +Lastly we're going to replace the default styles with some prewritten styles mentioned below. Similar to the smart contract section, we won't focus much on the CSS side of things but this will give you some basic layouts and center all of your content in the middle of the page. + +Replace the `global.css` and the `Home.module.css` with the styles below. + +
+ global.css + +```css +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} + +``` + +
+ +
+ Home.module.css + +```css +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #884c30; +} + +.linkWrapper { + margin-top: 15px; +} + +.read-the-docs:hover { + color: #b84814; + transition: 0.7s; +} + +.demoButton { + background-color: #884c30; + color: #fff; + margin-bottom: 5px; +} + +.demoButton:hover{ + background-color: #b84814; + transition: 0.7s; +} + +.viewNFT { + color: #b84814; +} +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.readDocs{ + color: #FF4E17; +} + +.linkWrapper { + margin-top: 15px; +} + +.readDocs:hover { + color: #b84814; + transition: 0.7s; +} + +.demoButton { + background-color: #FF4E17; + color: #fff; + margin-bottom: 5px; +} + +.demoButton:hover{ + background-color: #b84814; + transition: 0.7s; +} + +.viewNFT { + color: #FF4E17; +} + +.demoContainter { + text-align: center; + width: 100vw; +} + +.main { + text-align: center; + width: 100vw; +} + +.connect { + background-color: #FF4E17; + color: #fff; +} + + +``` + +
+ + diff --git a/docs/tutorials/nextjs/introduction.md b/docs/tutorials/nextjs/introduction.md new file mode 100644 index 00000000..245b63d8 --- /dev/null +++ b/docs/tutorials/nextjs/introduction.md @@ -0,0 +1,14 @@ +--- +sidebar_label: 'Introduction' +sidebar_position: 1 +--- + +# Introduction + +This tutorial will cover the basics of creating a dApp powered by the Biconomy SDK and utilizing Next JS and Particle Network. Here's an overview of what you'll learn: + +- Go over a basic contract for minting an NFT. +- Use Biconomy Dashboard for getting paymaster URL and bundler URL. +- Register the contract and its write methods on the Biconomy Dashboard. +- Create a frontend with Next JS and Particle network that aims to be friendly to use for web2 users onboarding to web3. + diff --git a/docs/tutorials/nextjs/nft.md b/docs/tutorials/nextjs/nft.md new file mode 100644 index 00000000..f44dd42d --- /dev/null +++ b/docs/tutorials/nextjs/nft.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 2 +--- + +# NFT Smart Contract + +The point of this tutorial is not to serve as another basic solidity tutorial but let's quickly review our basic NFT smart contract. Feel free to deploy your own version of this contract while completing this tutorial or simply use our implementation of it available [here](https://goerli.basescan.org/address/0x0a7755bDfb86109D9D403005741b415765EAf1Bc). This contract has been deployed on the Base Goerli test network. If you need Base Goerli Eth check out the [official documentation](https://docs.base.org/tools/network-faucets/) on how to get test funds. + +Let's take a look at NFT contract: + +```javascript + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; + +contract BasedBicoNFT is ERC721 { + using Counters for Counters.Counter; + + Counters.Counter private _tokenIdCounter; + + constructor() ERC721("Based Biconomy SDK NFT", "BBNFT") {} + + function safeMint(address to) public { + uint256 tokenId = _tokenIdCounter.current(); + _tokenIdCounter.increment(); + _safeMint(to, tokenId); + } +} + +``` + +This is a basic smart contract which inherits from ERC721 and simply allows the end user to mint an NFT using the defined safeMint function. It additionally assignes a token id to each minted NFT. + +Now with the initial smart contract ready, lets get our Paymaster and Bundler ready by using the Biconomy Dashboard. \ No newline at end of file diff --git a/docs/tutorials/nextjs/sdkintegration.md b/docs/tutorials/nextjs/sdkintegration.md new file mode 100644 index 00000000..c0cbf1f2 --- /dev/null +++ b/docs/tutorials/nextjs/sdkintegration.md @@ -0,0 +1,181 @@ +--- +sidebar_position: 5 +--- + +# SDK Integration + +## Remove unnecasary code + +Let's get started with working on the `index.tsx` file. + +In the jsx of the file let's remove all elements of the page except for the Head tags and Main tags. Your Component should look like this: + +```typescript +export default function Home() { + return ( + <> + + Based Account Abstraction + + +
+ +
+ + ) +} + +``` + +Notice I changed my title and description, feel free to do that. On the main tags I added a className for `styles.main` to get all my contect the centered look. + +## Set up Particle Auth + +Now we're going to leverage Particle Auth through the SDK to set up social logins. Remember we're focusing here on users who have never onboarded onto web3 via a wallet and just want o experience minting their first NFT. + +Let's import the Particle Auth Package + +```typescript +import { + ParticleAuthModule, + ParticleProvider, +} from "@biconomy/particle-auth"; +``` +In your React component define an instance of the Particle Auth Module. The module will require api keys which you can get from the [Particle Dashboard](https://docs.particle.network/getting-started/dashboard). + +```typescript + const particle = new ParticleAuthModule.ParticleNetwork({ + projectId: "", + clientKey: "", + appId: "", + wallet: { + displayWalletEntry: true, + defaultWalletEntryPosition: ParticleAuthModule.WalletEntryPosition.BR, + }, + }); +``` +I removed a couple extra items that might exist in their docs but those are all optional parameters, the paramaters listed above are the minimum ones you need to start the engine. + +Next lets get going with a connect function. This will contain the logic for logging in with the SDK. + +```typescript + + const connect = async () => { + try { + const userInfo = await particle.auth.login(); + console.log("Logged in user:", userInfo); + const particleProvider = new ParticleProvider(particle.auth); + console.log({particleProvider}) + const web3Provider = new ethers.providers.Web3Provider( + particleProvider, + "any" + ); + } catch (error) { + console.error(error); + } + }; + +``` + +Let's create a button in our component that will execute the above function on click: + +```typescript +
+ +
+``` + +We pass the connect function to the `onClick` handler and pass some more styles from our styles object. Try this out you now log in with several different social providers or via email with a one time password. General user information will be logged into the console upon succesful login. + +## Create a Smart Account + +Now that we have our login method enabled, lets use the Particle Network integration to now build our own smart account. + +Add the following imports to your `index.tsx`: + +```typescript + +import { useState } from 'react'; +import { IBundler, Bundler } from '@biconomy/bundler' +import { BiconomySmartAccount, BiconomySmartAccountConfig, DEFAULT_ENTRYPOINT_ADDRESS } from "@biconomy/account" +import { ethers } from 'ethers' +import { ChainId } from "@biconomy/core-types" +import { + IPaymaster, + BiconomyPaymaster, +} from '@biconomy/paymaster' + +``` +Now in the React component we're going to define the instance of our Bundler and Paymaster: + +```typescript + +const bundler: IBundler = new Bundler({ + bundlerUrl: // bundler URL from dashboard use 84531 as chain id if you are following this on base goerli, + chainId: ChainId.BASE_GOERLI_TESTNET, + entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + }) + + const paymaster: IPaymaster = new BiconomyPaymaster({ + paymasterUrl: // paymaster url from dashboard + }) + +``` + +We're also going to add some state variables to the component along with their typings: + +```typescript + const [address, setAddress] = useState("") + const [loading, setLoading] = useState(false); + const [smartAccount, setSmartAccount] = useState(null); + const [provider, setProvider] = useState(null) +``` + +Now we'll update the Connect function to create a smart account using the Biconomy Smart Account package: + +```typescript + +const connect = async () => { + try { + setLoading(true) + const userInfo = await particle.auth.login(); + console.log("Logged in user:", userInfo); + const particleProvider = new ParticleProvider(particle.auth); + const web3Provider = new ethers.providers.Web3Provider( + particleProvider, + "any" + ); + setProvider(web3Provider) + const biconomySmartAccountConfig: BiconomySmartAccountConfig = { + signer: web3Provider.getSigner(), + chainId: ChainId.BASE_GOERLI_TESTNET, + bundler: bundler, + paymaster: paymaster + } + let biconomySmartAccount = new BiconomySmartAccount(biconomySmartAccountConfig) + biconomySmartAccount = await biconomySmartAccount.init() + setAddress( await biconomySmartAccount.getSmartAccountAddress()) + setSmartAccount(biconomySmartAccount) + setLoading(false) + } catch (error) { + console.error(error); + } + }; + +``` +Now upon login we're also in the background creating a Smart Account for our users. Let's update the JSX in the component as well: + +```typescript +
+

Based Account Abstraction

+

Connect and Mint your AA powered NFT now

+ {!loading && !address && } + {loading &&

Loading Smart Account...

} + {address &&

Smart Account: {address}

} +
+``` + +Now when we login our Smart account address will be displayed for us on the screen. You'll notice that the main thing we need for interaction between Particle Auth and the Biconomy Smart Account is an ethers provider object. Keep this in mind if you want to use any other auth provider, as long as you can pass the ethers provider object your auth tool of choice will be compatible with the Biconomy SDK. + +You now have integrated the SDK along with Particle Auth for Social Logins. Lets now execute our transaction and allow the user of our dApp to mint an NFT completely for free. +