Skip to content

Commit 91a7cc0

Browse files
authored
Merge pull request #36 from androbwebb/webb/configurable-chain-with-env-var
feat: allow the chain to be configured through an env variable
2 parents 2f8b3e0 + b7b3b29 commit 91a7cc0

8 files changed

Lines changed: 110 additions & 14 deletions

File tree

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Retrieve your API Key and Policy ID from the smart wallets dashboard default configuration or create new keys: https://dashboard.alchemy.com/services/smart-wallets/configuration
22
NEXT_PUBLIC_ALCHEMY_API_KEY=YOUR_APP_API_KEY # Get your app API key: https://dashboard.alchemy.com/apps
33
NEXT_PUBLIC_ALCHEMY_POLICY_ID=YOUR_SPONSORSHIP_POLICY_ID # Get your gas sponsorship policy ID: https://dashboard.alchemy.com/services/gas-manager/configuration
4+
NEXT_PUBLIC_CHAIN_ID=421614 # Arbitrum Sepolia as default
45
# NOTE: make sure to set up a smart wallet configuration for your app to enable login

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ Use this template to get started with **embedded smart wallets** using [Alchemy
77
- Email, passkey & social login using pre‑built UI components
88
- Flexible, secure, and cheap smart accounts
99
- Gasless transactions powered by ERC-4337 Account Abstraction
10-
- One‑click NFT mint on Arbitrum Sepolia (no ETH required)
10+
- One‑click NFT mint (no ETH required)
1111
- Server‑side rendering ready – session persisted with cookies
1212
- TailwindCSS + shadcn/ui components, React Query, TypeScript
1313

1414
![Smart Wallet Quickstart](https://github.com/user-attachments/assets/2903fb78-e632-4aaa-befd-5775c60e1ca2)
1515

1616
## 📍 Network & Demo Contract
1717

18-
This quickstart is configured to run on **Arbitrum Sepolia** testnet. A free demo NFT contract has been deployed specifically for this quickstart, allowing you to mint NFTs without any setup or deployment steps. The contract is pre-configured and ready to use out of the box.
18+
This quickstart is configured to run on **Arbitrum Sepolia** testnet, by default. A free demo NFT contract has been deployed specifically for this quickstart, allowing you to mint NFTs without any setup or deployment steps. The contract is pre-configured and ready to use out of the box.
1919

2020
## 🚀 Quick start
2121

@@ -75,7 +75,7 @@ tailwind.config.ts
7575

7676
## 🏗️ How it works
7777

78-
1. `config.ts` initializes Account Kit with your API key, Arbitrum Sepolia chain, and Gas Sponsorship policy.
78+
1. `config.ts` initializes Account Kit with your API key, chain, and Gas Sponsorship policy.
7979
2. `Providers` wraps the app with `AlchemyAccountProvider` & React Query.
8080
3. `LoginCard` opens the authentication modal (`useAuthModal`).
8181
4. After login, `useSmartAccountClient` exposes the smart wallet.

app/components/nft-mint-card.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,17 @@ import Link from "next/link";
2121
import { useReadNFTData } from "@/app/hooks/useReadNFTData";
2222
import { useMint } from "@/app/hooks/useMintNFT";
2323
import { useSmartAccountClient } from "@account-kit/react";
24-
import { NFT_CONTRACT_ADDRESS } from "@/lib/constants";
24+
import { useNftContractAddress } from "@/app/hooks/useNftContractAddress";
2525

2626
export default function NftMintCard() {
2727
const [isImageLoading, setIsImageLoading] = useState(true);
2828
const [showSuccess, setShowSuccess] = useState(true);
29+
const nftContractAddress = useNftContractAddress();
2930

3031
const { client } = useSmartAccountClient({});
3132

3233
const { uri, count, isLoadingCount, refetchCount } = useReadNFTData({
33-
contractAddress: NFT_CONTRACT_ADDRESS,
34+
contractAddress: nftContractAddress,
3435
ownerAddress: client?.account?.address,
3536
});
3637

@@ -123,7 +124,7 @@ export default function NftMintCard() {
123124
)}
124125
>
125126
<PlusCircle className="h-[18px] w-[18px]" />
126-
Mint New NFT
127+
Mint New NFT {!!client?.chain?.name && `on ${client.chain.name}`}
127128
</span>
128129
<span
129130
className={cn(

app/hooks/useMintNFT.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
useSendUserOperation,
55
} from "@account-kit/react";
66
import { encodeFunctionData } from "viem";
7-
import { NFT_MINTABLE_ABI_PARSED, NFT_CONTRACT_ADDRESS } from "@/lib/constants";
7+
import { NFT_MINTABLE_ABI_PARSED } from "@/lib/constants";
8+
import { useNftContractAddress } from "@/app/hooks/useNftContractAddress";
89

910
export interface UseMintNFTParams {
1011
onSuccess?: () => void;
@@ -19,6 +20,7 @@ export interface UseMintReturn {
1920
export const useMint = ({ onSuccess }: UseMintNFTParams): UseMintReturn => {
2021
const [isMinting, setIsMinting] = useState(false);
2122
const [error, setError] = useState<string>();
23+
const nftContractAddress = useNftContractAddress();
2224

2325
const { client } = useSmartAccountClient({});
2426

@@ -51,17 +53,22 @@ export const useMint = ({ onSuccess }: UseMintNFTParams): UseMintReturn => {
5153
return;
5254
}
5355

56+
if (!nftContractAddress) {
57+
setError("Contract address is not defined.");
58+
return;
59+
}
60+
5461
sendUserOperation({
5562
uo: {
56-
target: NFT_CONTRACT_ADDRESS,
63+
target: nftContractAddress,
5764
data: encodeFunctionData({
5865
abi: NFT_MINTABLE_ABI_PARSED,
5966
functionName: "mintTo",
6067
args: [client.getAddress()],
6168
}),
6269
},
6370
});
64-
}, [client, sendUserOperation]);
71+
}, [client, sendUserOperation, nftContractAddress]);
6572

6673
const transactionUrl = useMemo(() => {
6774
if (!client?.chain?.blockExplorers || !sendUserOperationResult?.hash) {

app/hooks/useNftContractAddress.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { useSmartAccountClient } from "@account-kit/react";
2+
import { arbitrumSepolia } from "@account-kit/infra";
3+
import { ChainData, chainNFTMintContractData } from "@/lib/chains";
4+
5+
export const useNftContractAddress = (): ChainData['nftContractAddress'] | undefined => {
6+
const { client } = useSmartAccountClient({});
7+
const chain = client?.chain || arbitrumSepolia;
8+
9+
return chainNFTMintContractData[chain.id]?.nftContractAddress;
10+
}

config.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import {
33
cookieStorage,
44
createConfig,
55
} from "@account-kit/react";
6-
import { alchemy, arbitrumSepolia } from "@account-kit/infra";
76
import { QueryClient } from "@tanstack/react-query";
7+
import { chainNFTMintContractData } from "@/lib/chains";
8+
import { alchemy } from "@account-kit/infra";
89

910
const API_KEY = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY;
1011
if (!API_KEY) {
@@ -16,6 +17,12 @@ if (!SPONSORSHIP_POLICY_ID) {
1617
throw new Error("NEXT_PUBLIC_ALCHEMY_POLICY_ID is not set");
1718
}
1819

20+
const CHAIN_ID = parseInt(process.env.NEXT_PUBLIC_CHAIN_ID ?? '') || 421614;
21+
const chain = chainNFTMintContractData[CHAIN_ID]?.chain;
22+
if (!chain) {
23+
throw new Error("Invalid chain ID")
24+
}
25+
1926
const uiConfig: AlchemyAccountsUIConfig = {
2027
illustrationStyle: "outline",
2128
auth: {
@@ -34,8 +41,7 @@ const uiConfig: AlchemyAccountsUIConfig = {
3441
export const config = createConfig(
3542
{
3643
transport: alchemy({ apiKey: API_KEY }),
37-
// Note: This quickstart is configured for Arbitrum Sepolia.
38-
chain: arbitrumSepolia,
44+
chain,
3945
ssr: true, // more about ssr: https://www.alchemy.com/docs/wallets/react/ssr
4046
storage: cookieStorage, // more about persisting state with cookies: https://www.alchemy.com/docs/wallets/react/ssr#persisting-the-account-state
4147
enablePopupOauth: true, // must be set to "true" if you plan on using popup rather than redirect in the social login flow

lib/chains.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Address, Chain } from "viem";
2+
import {
3+
arbitrumSepolia,
4+
baseSepolia,
5+
sepolia,
6+
polygonAmoy,
7+
shapeSepolia,
8+
soneiumMinato,
9+
unichainSepolia,
10+
inkSepolia,
11+
monadTestnet,
12+
riseTestnet,
13+
storyAeneid,
14+
teaSepolia
15+
} from "@account-kit/infra";
16+
17+
export interface ChainData {
18+
chain: Chain;
19+
nftContractAddress: Address;
20+
}
21+
22+
export const chainNFTMintContractData: Record<Chain['id'], ChainData> = {
23+
[arbitrumSepolia.id]: {
24+
chain: arbitrumSepolia,
25+
nftContractAddress: "0x6D1BaA7951f26f600b4ABc3a9CF8F18aBf36fac1",
26+
},
27+
[baseSepolia.id]: {
28+
chain: baseSepolia,
29+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
30+
},
31+
[polygonAmoy.id]: {
32+
chain: polygonAmoy,
33+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
34+
},
35+
[sepolia.id]: {
36+
chain: sepolia,
37+
nftContractAddress: "0xc59b508C90425C8e25e3F9dA30e52057908E2838",
38+
},
39+
[shapeSepolia.id]: {
40+
chain: shapeSepolia,
41+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
42+
},
43+
[soneiumMinato.id]: {
44+
chain: soneiumMinato,
45+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
46+
},
47+
[unichainSepolia.id]: {
48+
chain: unichainSepolia,
49+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
50+
},
51+
[inkSepolia.id]: {
52+
chain: inkSepolia,
53+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
54+
},
55+
[monadTestnet.id]: {
56+
chain: monadTestnet,
57+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
58+
},
59+
[riseTestnet.id]: {
60+
chain: riseTestnet,
61+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
62+
},
63+
[storyAeneid.id]: {
64+
chain: storyAeneid,
65+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
66+
},
67+
[teaSepolia.id]: {
68+
chain: teaSepolia,
69+
nftContractAddress: "0x6d15c130d9B2548597C1d2D0c8CB2067Ce9C4525",
70+
},
71+
}

lib/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { parseAbi } from "viem";
2+
import { arbitrumSepolia } from "@account-kit/infra";
23

3-
export const NFT_CONTRACT_ADDRESS =
4-
"0x6D1BaA7951f26f600b4ABc3a9CF8F18aBf36fac1";
4+
export const DEFAULT_CHAIN_ID = arbitrumSepolia.id;
55

66
export const NFT_MINTABLE_ABI_PARSED = parseAbi([
77
"function mintTo(address recipient) returns (uint256)",

0 commit comments

Comments
 (0)