Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
27 changes: 27 additions & 0 deletions .changeset/silent-areas-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
"thirdweb": minor
---

EIP7702 support for in-app wallets

You can now turn your in-app wallets into smart accounts with 7702!

This lets you:

- sponsor transactions
- batch transactions
- add session keys
- and more!

simply pass the executionMode "EIP7702" to get started:

```ts
const wallet = inAppWallet({
executionMode: {
mode: "EIP7702",
sponsorGas: true,
},
});
```

Keep in mind that this will only work on chains that support 7702.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import ThirdwebProvider from "@/components/thirdweb-provider";
import { metadataBase } from "@/lib/constants";
import type { Metadata } from "next";
import { Eip7702SmartAccountPreview } from "../../../../components/account-abstraction/7702-smart-account";
import { PageLayout } from "../../../../components/blocks/APIHeader";
import { CodeExample } from "../../../../components/code/code-example";

export const metadata: Metadata = {
metadataBase,
title: "EIP-7702 Smart Accounts | thirdweb Connect",
description:
"EIP-7702 smart accounts allow you to turn your EOA into a smart account with no code changes",
};

export default function Page() {
return (
<ThirdwebProvider>
<PageLayout
title="EIP-7702 Smart Accounts"
description={
<>
EIP-7702 smart accounts allow you to turn your EOA into a smart
account with no code changes.
</>
}
docsLink="https://portal.thirdweb.com/connect/account-abstraction/overview?utm_source=playground"
>
<Eip7702SmartAccount />
</PageLayout>
</ThirdwebProvider>
);
}

function Eip7702SmartAccount() {
return (
<>
<CodeExample
preview={<Eip7702SmartAccountPreview />}
code={`\
import { claimTo } from "thirdweb/extensions/erc1155";
import { TransactionButton } from "thirdweb/react";
const wallet = inAppWallet({
executionMode: {
mode: "EIP7702",
sponsorGas: true,
},
});
function App() {
return (
<>
<ConnectButton
client={client}
wallet={[wallet]}
connectButton={{
label: "Login to mint!",
}}
/>
{/* since sponsorGas is true, transactions will be sponsored */}
<TransactionButton
transaction={() =>
claimTo({
contract,
to: "0x123...",
tokenId: 0n,
quantity: 1n,
})
}
>
Mint
</TransactionButton>
</>
);
}`}
lang="tsx"
/>
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { CodeExample } from "../../../../components/code/code-example";

export const metadata: Metadata = {
metadataBase,
title: "Sign In, Account Abstraction and SIWE Auth | thirdweb Connect",
title: "Account Abstraction | thirdweb Connect",
description:
"Let users sign up with their email, phone number, social media accounts or directly with a wallet. Seamlessly integrate account abstraction and SIWE auth.",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ import { CodeExample } from "../../../../components/code/code-example";

export const metadata: Metadata = {
metadataBase,
title: "Sponsored transactions | thirdweb Connect",
description:
"Easily enable gas-free transactions for your users, Free on testnets, billed at the end of the month on mainnets.",
title: "EIP-4337 Smart Contract Wallets | thirdweb Connect",
description: "Turn any EOA into a smart contract wallet with EIP-4337.",
};

export default function Page() {
return (
<ThirdwebProvider>
<PageLayout
title="Sponsored transactions"
title="EIP-4337 Smart Contract Wallets"
description={
<>
Easily enable gas-free transactions for your users, Free on
testnets, billed at the end of the month on mainnets.
</>
<>Turn any EOA into a smart contract wallet with EIP-4337.</>
}
docsLink="https://portal.thirdweb.com/connect/account-abstraction/overview?utm_source=playground"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,19 @@ import {
ConnectButton,
TransactionButton,
} from "thirdweb/react";
import { baseSepolia } from "thirdweb/chains";
const wallets = [
inAppWallet(
// turn on gas sponsorship for in-app wallets
{ smartAccount: { chain, sponsorGas: true } },
),
];
{
// turn on gas sponsorship for in-app wallets
// Can use EIP4337 or EIP7702 on supported chains
executionMode: {
mode: "EIP4337",
smartAccount: { chain: baseSepolia, sponsorGas: true },
},
}),
];
function App() {
return (
Expand Down
8 changes: 6 additions & 2 deletions apps/playground-web/src/app/navLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ const staticSidebarLinks: SidebarLink[] = [
href: "/connect/account-abstraction/connect",
},
{
name: "Sponsor Gas",
name: "EIP-4337",
href: "/connect/account-abstraction/sponsor",
},
{
name: "Native AA (zkSync)",
name: "EIP-7702",
href: "/connect/account-abstraction/7702",
},
{
name: "Native (zkSync)",
href: "/connect/account-abstraction/native-aa",
},
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"use client";

import { useState } from "react";
import { getContract } from "thirdweb";
import { sepolia } from "thirdweb/chains";
import { claimTo } from "thirdweb/extensions/erc1155";
import { getNFT, getOwnedNFTs } from "thirdweb/extensions/erc1155";
import {
ConnectButton,
MediaRenderer,
TransactionButton,
useActiveAccount,
useReadContract,
} from "thirdweb/react";
import { shortenHex } from "thirdweb/utils";
import { inAppWallet } from "thirdweb/wallets/in-app";
import { THIRDWEB_CLIENT } from "../../lib/client";

const chain = sepolia;
const editionDropAddress = "0x7B3e0B8353Ad5cD6C60355B50550F63335752f9F";
const editionDropTokenId = 1n;

const editionDropContract = getContract({
address: editionDropAddress,
chain,
client: THIRDWEB_CLIENT,
});

const iaw = inAppWallet({
executionMode: {
mode: "EIP7702",
sponsorGas: true,
},
});

export function Eip7702SmartAccountPreview() {
const [txHash, setTxHash] = useState<string | null>(null);
const activeEOA = useActiveAccount();
const { data: nft, isLoading: isNftLoading } = useReadContract(getNFT, {
contract: editionDropContract,
tokenId: editionDropTokenId,
});
const { data: ownedNfts } = useReadContract(getOwnedNFTs, {
contract: editionDropContract,
useIndexer: false,
// biome-ignore lint/style/noNonNullAssertion: handled by queryOptions
address: activeEOA?.address!,
queryOptions: { enabled: !!activeEOA },
});

return (
<div className="flex flex-col items-center justify-center gap-4">
{isNftLoading ? (
<div className="mt-24 w-full">Loading...</div>
) : (
<>
<div className="flex flex-col justify-center gap-2 p-2">
<ConnectButton
client={THIRDWEB_CLIENT}
chain={sepolia}
wallets={[iaw]}
connectButton={{
label: "Login to mint!",
}}
/>
</div>
{nft ? (
<MediaRenderer
client={THIRDWEB_CLIENT}
src={nft.metadata.image}
style={{ width: "300px", marginTop: "10px" }}
/>
) : null}
{activeEOA ? (
<div className="flex flex-col justify-center gap-4 p-2">
<p className="mb-2 text-center font-semibold">
You own {ownedNfts?.[0]?.quantityOwned.toString() || "0"}{" "}
{nft?.metadata?.name}
</p>
<TransactionButton
transaction={() =>
claimTo({
contract: editionDropContract,
tokenId: editionDropTokenId,
to: activeEOA.address,
quantity: 1n,
})
}
payModal={{
metadata: nft?.metadata,
}}
onError={(error) => {
alert(`Error: ${error.message}`);
}}
onClick={() => {
setTxHash(null);
}}
onTransactionSent={async (tx) => {
setTxHash(tx.transactionHash);
}}
>
Mint with EIP-7702
</TransactionButton>
</div>
) : null}
{txHash ? (
<div className="flex flex-col justify-center p-2">
<p className="mb-2 text-center text-green-500">
Minted! Tx Hash:{" "}
<a
href={`${chain.blockExplorers?.[0]?.url}/tx/${txHash}`}
target="_blank"
rel="noopener noreferrer"
className="underline"
>
{shortenHex(txHash)}
</a>
</p>
</div>
) : null}
</>
)}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ export function SponsoredTxPreview() {
contract: editionDropContract,
// biome-ignore lint/style/noNonNullAssertion: handled by queryOptions
address: smartAccount?.address!,
useIndexer: false,
queryOptions: { enabled: !!smartAccount },
});

return (
<div className="flex flex-col items-center justify-center">
<div className="flex flex-col items-center justify-center gap-4">
{isNftLoading ? (
<div className="mt-24 w-full">Loading...</div>
) : (
Expand All @@ -63,14 +64,14 @@ export function SponsoredTxPreview() {
<MediaRenderer
client={THIRDWEB_CLIENT}
src={nft.metadata.image}
style={{ width: "400px", marginTop: "10px" }}
style={{ width: "300px", marginTop: "10px" }}
/>
) : null}
{smartAccount ? (
<div className="flex flex-col justify-center p-2">
<p className="mb-2 text-center font-semibold">
You own {ownedNfts?.[0]?.quantityOwned.toString() || "0"}{" "}
Kittens
{nft?.metadata?.name}
</p>
<TransactionButton
transaction={() =>
Expand All @@ -94,7 +95,7 @@ export function SponsoredTxPreview() {
setTxHash(receipt.transactionHash);
}}
>
Mint
Mint with EIP-4337
</TransactionButton>
</div>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ export function SponsoredInAppTxPreview() {
"guest",
],
},
smartAccount: {
chain: baseSepolia,
sponsorGas: true,
// TODO (7702): update to 7702 once pectra is out
executionMode: {
mode: "EIP4337",
smartAccount: {
chain: baseSepolia,
sponsorGas: true,
},
},
}),
]}
Expand Down
2 changes: 1 addition & 1 deletion packages/thirdweb/.size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
{
"name": "thirdweb (cjs)",
"path": "./dist/cjs/exports/thirdweb.js",
"limit": "150 kB"
"limit": "200 kB"
},
{
"name": "thirdweb (minimal + tree-shaking)",
Expand Down
Loading
Loading