Skip to content

Commit

Permalink
feat: sessionStorage for burner wallet (#24)
Browse files Browse the repository at this point in the history
Co-authored-by: Shiv Bhonde <shivbhonde04@gmail.com>
  • Loading branch information
rin-st and technophile-04 authored Dec 20, 2024
1 parent 85c0e37 commit 1849aec
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-wasps-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"burner-connector": patch
---

feat: sessionStorage for burner wallet
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pnpm run dev

This will start a local server on `http://localhost:3000` with the example app linked to local package

The burner wallet should be automatically connected to sepolia network, and can interact with the [`YourContract`](https://sepolia.etherscan.io/address/0x0D25b202D1B5126ECFcaeFa85f7a37ed86EF79ea) deployed on the sepolia.
The burner wallet should be automatically connected to optimism sepolia network, and can interact with the [`YourContract`](https://optimism-sepolia.blockscout.com/address/0xFB30C0790128b97e3aC540E6124e512E37c47D00).

## Testing with hardhat

Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ pnpm add burner-connector
```ts
import { burner } from "burner-connector";
import { mainnet, base } from "viem/chains";

// burner function can also called with param `{ useSessionStorage: true }` to create a new wallet for each browser tab
// - `useSessionStorage` to false (default) to persist wallet across browser tabs(incognito window will have different wallet)
// - `useSessionStorage` to true to create a new wallet for each browser tab

export const config = createConfig({
chains: [mainnet, base],
connectors: [burner()],
Expand All @@ -49,6 +54,13 @@ import { mainnet, base } from "viem/chains";

const wallets = [metaMaskWallet, rainbowkitBurnerWallet];

// Configure burner wallet storage
// - `useSessionStorage` to false (default) to persist wallet across browser tabs(incognito window will have different wallet)
// - `useSessionStorage` to true to create a new wallet for each browser tab
// rainbowkitBurnerWallet.useSessionStorage = true;

const wallets = [metaMaskWallet, rainbowkitBurnerWallet];

const wagmiConnectors = connectorsForWallets(
[
{
Expand Down
6 changes: 4 additions & 2 deletions example/app/components/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { FaucetButton } from "./FaucetButton";
import toast from "react-hot-toast";
import deployedContracts from "../contracts/deployedContracts";
import { optimismSepolia } from "viem/chains";

export const Example = () => {
const { isConnected, chain } = useAccount();
Expand All @@ -17,8 +18,9 @@ export const Example = () => {
console.log("isPending", isPending);
const yourContract =
chain?.id && chain.id in deployedContracts
? deployedContracts[chain.id as 11155111 | 31337].YourContract
: deployedContracts["11155111"].YourContract;
? deployedContracts[chain.id as keyof typeof deployedContracts]
.YourContract
: deployedContracts[optimismSepolia.id].YourContract;

const { data: totalCounter } = useReadContract({
...yourContract,
Expand Down
4 changes: 2 additions & 2 deletions example/app/contracts/deployedContracts.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const deployedContracts = {
11155111: {
11155420: {
YourContract: {
address: "0x0D25b202D1B5126ECFcaeFa85f7a37ed86EF79ea",
address: "0xFB30C0790128b97e3aC540E6124e512E37c47D00",
abi: [
{
inputs: [
Expand Down
9 changes: 6 additions & 3 deletions example/app/wagmiConfig.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { connectorsForWallets } from "@rainbow-me/rainbowkit";
import { createConfig } from "wagmi";
import { hardhat, sepolia } from "wagmi/chains";
import { hardhat, optimismSepolia } from "wagmi/chains";
import { metaMaskWallet } from "@rainbow-me/rainbowkit/wallets";
import { createClient, http } from "viem";
import { rainbowkitBurnerWallet } from "burner-connector";

// Use this if you want to enable session storage
// rainbowkitBurnerWallet.useSessionStorage = true;

const wallets = [metaMaskWallet, rainbowkitBurnerWallet];
const walletConnectProjectID = "3a8170812b534d0ff9d794f19a901d64";
const wagmiConnectors = connectorsForWallets(
Expand All @@ -18,10 +21,10 @@ const wagmiConnectors = connectorsForWallets(
{
appName: "scaffold-eth-2",
projectId: walletConnectProjectID,
},
}
);

export const chains = [sepolia, hardhat] as const;
export const chains = [optimismSepolia, hardhat] as const;

export const wagmiConfig = createConfig({
chains: chains,
Expand Down
4 changes: 2 additions & 2 deletions packages/burner-connector/src/burnerConnector/burner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class ChainNotConfiguredError extends BaseError {

type Provider = ReturnType<Transport<"custom", Record<any, any>, EIP1193RequestFn<WalletRpcSchema>>>;

export const burner = () => {
export const burner = ({ useSessionStorage = false }: { useSessionStorage?: boolean } = {}) => {
let connected = true;
let connectedChainId: number;
return createConnector<Provider>((config) => ({
Expand All @@ -55,7 +55,7 @@ export const burner = () => {

const url = chain.rpcUrls.default.http[0];
if (!url) throw new Error("No rpc url found for chain");
const burnerAccount = privateKeyToAccount(loadBurnerPK());
const burnerAccount = privateKeyToAccount(loadBurnerPK({ useSessionStorage }));
const client = createWalletClient({
chain: chain,
account: burnerAccount,
Expand Down
24 changes: 16 additions & 8 deletions packages/burner-connector/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,36 @@ const isValidPK = (pk: Hex | string | undefined | null): boolean => {
};

/**
* Save the current burner private key to local storage
* Save the current burner private key to storage
*/
export const saveBurnerPK = (privateKey: Hex): void => {
const saveBurnerPK = ({
privateKey,
useSessionStorage = false,
}: {
privateKey: Hex;
useSessionStorage?: boolean;
}): void => {
if (typeof window !== "undefined" && window != null) {
window?.localStorage?.setItem(burnerStorageKey, privateKey);
const storage = useSessionStorage ? window.sessionStorage : window.localStorage;
storage?.setItem(burnerStorageKey, privateKey);
}
};

/**
* Gets the current burner private key from local storage
* Gets the current burner private key from local/session storage
*/
export const loadBurnerPK = (): Hex => {
export const loadBurnerPK = ({ useSessionStorage = false }: { useSessionStorage?: boolean } = {}): Hex => {
let currentSk: Hex = "0x";
if (typeof window !== "undefined" && window != null) {
currentSk = (window?.localStorage?.getItem?.(burnerStorageKey)?.replaceAll('"', "") ?? "0x") as Hex;
const storage = useSessionStorage ? window.sessionStorage : window.localStorage;
currentSk = (storage?.getItem?.(burnerStorageKey)?.replaceAll('"', "") ?? "0x") as Hex;
}

if (!!currentSk && isValidPK(currentSk)) {
return currentSk;
}
// If no burner is found in localstorage, we will generate a random private key
// If no burner is found in storage, we will generate a random private key
const newDefaultPrivateKey = generatePrivateKey();
saveBurnerPK(newDefaultPrivateKey);
saveBurnerPK({ privateKey: newDefaultPrivateKey, useSessionStorage });
return newDefaultPrivateKey;
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ export const rainbowkitBurnerConnector = (walletDetails: WalletDetailsParams) =>
...walletDetails,
}));
};

export const rainbowkitSessionStorageBurnerConnector = (walletDetails: WalletDetailsParams) => {
return createConnector<Provider>((config) => ({
...burner({ useSessionStorage: true })(config),
...walletDetails,
}));
};
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
import type { Wallet } from "@rainbow-me/rainbowkit";
import { burnerWalletId, burnerWalletName } from "../../utils/index.js";
import { rainbowkitBurnerConnector } from "./rainbowkitBurnerConnector.js";
import { rainbowkitBurnerConnector, rainbowkitSessionStorageBurnerConnector } from "./rainbowkitBurnerConnector.js";

const burnerWalletIconBase64 =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzUzIiBoZWlnaHQ9IjM1MiIgdmlld0JveD0iMCAwIDM1MyAzNTIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9IjAuNzE2MzA5IiB5PSIwLjMxNzEzOSIgd2lkdGg9IjM1MS4zOTQiIGhlaWdodD0iMzUxLjM5NCIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzNfMTUxKSIvPgo8Y2lyY2xlIGN4PSIzNC40OTUzIiBjeT0iMzQuNDk1MyIgcj0iMzQuNDk1MyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjA3LjAxOCAyNTQuMTIpIiBmaWxsPSIjRkY2NjBBIi8+CjxwYXRoIGQ9Ik0xNTQuMzE4IDMxNy45NTVDMTcxLjI3MyAzMTAuODkgMTc2LjU4MiAyOTAuNzE1IDE3Ni4xNTcgMjgzLjQ4N0wyMDcuMDE4IDI4OC44NjRDMjA3LjAxOCAzMDMuMzE0IDIwMC4yMTIgMzA5LjQwMiAxOTcuODI0IDMxMi40MzNDMTkzLjQ3NCAzMTcuOTU1IDE3My4zNTEgMzMwLjAzIDE1NC4zMTggMzE3Ljk1NVoiIGZpbGw9InVybCgjcGFpbnQxX3JhZGlhbF8zXzE1MSkiLz4KPGcgZmlsdGVyPSJ1cmwoI2ZpbHRlcjBfZF8zXzE1MSkiPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIyNy4zNzcgMzAyLjI3NkMyMjYuNDI2IDMwNS44OTcgMjMwLjMxNSAzMDkuNDA1IDIzMy4zOTYgMzA3LjI3OUMyNTQuNTM4IDI5Mi42ODQgMjcwLjQ3OSAyNjkuOTQ1IDI3NC44OSAyNDcuNDg5QzI4Mi4yNCAyMTAuMDcxIDI3Mi4yMzUgMTc1LjcyNyAyMzguMDI4IDE0NS45MjVDMjAwLjg3NCAxMTMuNTU2IDE5MS44NDQgODguNDU2MSAxOTAuMTYyIDUwLjg3MThDMTg5Ljc5NyA0Mi43MjE4IDE4MS42MDQgMzcuMjk0NyAxNzQuODI0IDQxLjgzMTdDMTUyLjY2OCA1Ni42NTc0IDEzMi41MTIgODQuNDk5IDEzOC45MTEgMTIwLjc1OEMxNDEuMDA0IDEzMi42MjEgMTQ2Ljc5NCAxNDEuMDE2IDE1MS45NyAxNDguNTIzQzE1OC40OTEgMTU3Ljk3OCAxNjQuMDM5IDE2Ni4wMjMgMTU5Ljk5NyAxNzcuODFDMTU1LjIwMyAxOTEuNzk0IDEzOS4xMzQgMTk5LjE2MiAxMjguNzQ3IDE5Mi40MjlDMTE0LjE3IDE4Mi45ODEgMTEzLjI1MyAxNjYuNjUxIDExNy45NjkgMTQ5LjQ1NkMxMTguOTAyIDE0Ni4wNTUgMTE1LjQ3MSAxNDMuMjA0IDExMi42OCAxNDUuMzU5QzkxLjM2MDQgMTYxLjgyMSA2OS4xNTMyIDE5OS4yNjcgNzcuNjY0NyAyNDcuNDg5Qzg1Ljk3OTIgMjc2LjIxMiA5Ny45Mjc3IDI5Mi41MzcgMTEwLjk3MSAzMDEuNTQxQzExMy43NjMgMzAzLjQ2OCAxMTcuMTU5IDMwMC42MzEgMTE2LjU5NyAyOTcuMjg2QzExNi4wODEgMjk0LjIxMiAxMTUuODEzIDI5MS4wNTQgMTE1LjgxMyAyODcuODMzQzExNS44MTMgMjU2LjUxMyAxNDEuMjAzIDIzMS4xMjMgMTcyLjUyMyAyMzEuMTIzQzIwMy44NDIgMjMxLjEyMyAyMjkuMjMyIDI1Ni41MTMgMjI5LjIzMiAyODcuODMzQzIyOS4yMzIgMjkyLjgyNCAyMjguNTg3IDI5Ny42NjUgMjI3LjM3NyAzMDIuMjc2WiIgZmlsbD0idXJsKCNwYWludDJfbGluZWFyXzNfMTUxKSIvPgo8L2c+CjxkZWZzPgo8ZmlsdGVyIGlkPSJmaWx0ZXIwX2RfM18xNTEiIHg9IjcyLjExMTIiIHk9IjM2LjQ5NCIgd2lkdGg9IjIwOC43NDIiIGhlaWdodD0iMjc1LjEyIiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiI+CjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+CjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VBbHBoYSIgdHlwZT0ibWF0cml4IiB2YWx1ZXM9IjAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDEyNyAwIiByZXN1bHQ9ImhhcmRBbHBoYSIvPgo8ZmVPZmZzZXQvPgo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIxLjg0NTA2Ii8+CjxmZUNvbXBvc2l0ZSBpbjI9ImhhcmRBbHBoYSIgb3BlcmF0b3I9Im91dCIvPgo8ZmVDb2xvck1hdHJpeCB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAxIDAgMCAwIDAgMC40MiAwIDAgMCAwIDAgMCAwIDAgMC43IDAiLz4KPGZlQmxlbmQgbW9kZT0ibXVsdGlwbHkiIGluMj0iQmFja2dyb3VuZEltYWdlRml4IiByZXN1bHQ9ImVmZmVjdDFfZHJvcFNoYWRvd18zXzE1MSIvPgo8ZmVCbGVuZCBtb2RlPSJub3JtYWwiIGluPSJTb3VyY2VHcmFwaGljIiBpbjI9ImVmZmVjdDFfZHJvcFNoYWRvd18zXzE1MSIgcmVzdWx0PSJzaGFwZSIvPgo8L2ZpbHRlcj4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzNfMTUxIiB4MT0iMTc2LjQxMyIgeTE9IjAuMzE3MTM5IiB4Mj0iMTc2LjQxMyIgeTI9IjM1MS43MTEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRjI3OSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNGRkQzMzYiLz4KPC9saW5lYXJHcmFkaWVudD4KPHJhZGlhbEdyYWRpZW50IGlkPSJwYWludDFfcmFkaWFsXzNfMTUxIiBjeD0iMCIgY3k9IjAiIHI9IjEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBncmFkaWVudFRyYW5zZm9ybT0idHJhbnNsYXRlKDIxOC4wNDggMjQ5LjM0Nykgcm90YXRlKDEyNC4wMTgpIHNjYWxlKDg5LjI5NTUgMjY0LjgwOSkiPgo8c3RvcCBvZmZzZXQ9IjAuNjQwODUiIHN0b3AtY29sb3I9IiNGRjY2MEEiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkZCRTE1Ii8+CjwvcmFkaWFsR3JhZGllbnQ+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQyX2xpbmVhcl8zXzE1MSIgeDE9IjE3Ni40ODIiIHkxPSI0MC4xODQxIiB4Mj0iMTc2LjQ4MiIgeTI9IjMxNy4yNzgiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjMzODU0MiIgc3RvcC1jb2xvcj0iI0ZGOEYzRiIvPgo8c3RvcCBvZmZzZXQ9IjAuNjU2MjUiIHN0b3AtY29sb3I9IiNGRjcwMjAiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRkYzRDAwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg==";

type RainbowkitBurnerWallet = {
(): Wallet;
useSessionStorage?: boolean;
};

/**
* Wagmi config for burner wallet
*/
export const rainbowkitBurnerWallet = (): Wallet => ({
export const rainbowkitBurnerWallet: RainbowkitBurnerWallet = () => ({
id: burnerWalletId,
name: burnerWalletName,
iconUrl: burnerWalletIconBase64,
iconBackground: "#ffffff",
createConnector: rainbowkitBurnerConnector,
createConnector: rainbowkitBurnerWallet.useSessionStorage
? rainbowkitSessionStorageBurnerConnector
: rainbowkitBurnerConnector,
});

0 comments on commit 1849aec

Please sign in to comment.