Skip to content

Commit e96ebba

Browse files
[SDK] Add 7702 support + minimal account implementation (#6874)
1 parent 4e6daac commit e96ebba

File tree

334 files changed

+3066
-76
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

334 files changed

+3066
-76
lines changed

.changeset/silent-areas-melt.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
"thirdweb": minor
3+
---
4+
5+
EIP7702 support for in-app wallets
6+
7+
You can now turn your in-app wallets into smart accounts with 7702!
8+
9+
This lets you:
10+
11+
- sponsor transactions
12+
- batch transactions
13+
- add session keys
14+
- and more!
15+
16+
simply pass the executionMode "EIP7702" to get started:
17+
18+
```ts
19+
const wallet = inAppWallet({
20+
executionMode: {
21+
mode: "EIP7702",
22+
sponsorGas: true,
23+
},
24+
});
25+
```
26+
27+
Keep in mind that this will only work on chains that support 7702.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import ThirdwebProvider from "@/components/thirdweb-provider";
2+
import { metadataBase } from "@/lib/constants";
3+
import type { Metadata } from "next";
4+
import { Eip7702SmartAccountPreview } from "../../../../components/account-abstraction/7702-smart-account";
5+
import { PageLayout } from "../../../../components/blocks/APIHeader";
6+
import { CodeExample } from "../../../../components/code/code-example";
7+
8+
export const metadata: Metadata = {
9+
metadataBase,
10+
title: "EIP-7702 Smart Accounts | thirdweb Connect",
11+
description:
12+
"EIP-7702 smart accounts allow you to turn your EOA into a smart account with no code changes",
13+
};
14+
15+
export default function Page() {
16+
return (
17+
<ThirdwebProvider>
18+
<PageLayout
19+
title="EIP-7702 Smart Accounts"
20+
description={
21+
<>
22+
EIP-7702 smart accounts allow you to turn your EOA into a smart
23+
account with no code changes.
24+
</>
25+
}
26+
docsLink="https://portal.thirdweb.com/connect/account-abstraction/overview?utm_source=playground"
27+
>
28+
<Eip7702SmartAccount />
29+
</PageLayout>
30+
</ThirdwebProvider>
31+
);
32+
}
33+
34+
function Eip7702SmartAccount() {
35+
return (
36+
<>
37+
<CodeExample
38+
preview={<Eip7702SmartAccountPreview />}
39+
code={`\
40+
import { claimTo } from "thirdweb/extensions/erc1155";
41+
import { TransactionButton } from "thirdweb/react";
42+
43+
const wallet = inAppWallet({
44+
executionMode: {
45+
mode: "EIP7702",
46+
sponsorGas: true,
47+
},
48+
});
49+
50+
function App() {
51+
return (
52+
<>
53+
<ConnectButton
54+
client={client}
55+
wallet={[wallet]}
56+
connectButton={{
57+
label: "Login to mint!",
58+
}}
59+
/>
60+
{/* since sponsorGas is true, transactions will be sponsored */}
61+
<TransactionButton
62+
transaction={() =>
63+
claimTo({
64+
contract,
65+
to: "0x123...",
66+
tokenId: 0n,
67+
quantity: 1n,
68+
})
69+
}
70+
>
71+
Mint
72+
</TransactionButton>
73+
</>
74+
);
75+
}`}
76+
lang="tsx"
77+
/>
78+
</>
79+
);
80+
}

apps/playground-web/src/app/connect/account-abstraction/connect/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { CodeExample } from "../../../../components/code/code-example";
1010

1111
export const metadata: Metadata = {
1212
metadataBase,
13-
title: "Sign In, Account Abstraction and SIWE Auth | thirdweb Connect",
13+
title: "Account Abstraction | thirdweb Connect",
1414
description:
1515
"Let users sign up with their email, phone number, social media accounts or directly with a wallet. Seamlessly integrate account abstraction and SIWE auth.",
1616
};

apps/playground-web/src/app/connect/account-abstraction/sponsor/page.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,17 @@ import { CodeExample } from "../../../../components/code/code-example";
77

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

1514
export default function Page() {
1615
return (
1716
<ThirdwebProvider>
1817
<PageLayout
19-
title="Sponsored transactions"
18+
title="EIP-4337 Smart Contract Wallets"
2019
description={
21-
<>
22-
Easily enable gas-free transactions for your users, Free on
23-
testnets, billed at the end of the month on mainnets.
24-
</>
20+
<>Turn any EOA into a smart contract wallet with EIP-4337.</>
2521
}
2622
docsLink="https://portal.thirdweb.com/connect/account-abstraction/overview?utm_source=playground"
2723
>

apps/playground-web/src/app/connect/in-app-wallet/sponsor/page.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@ import {
4545
ConnectButton,
4646
TransactionButton,
4747
} from "thirdweb/react";
48+
import { baseSepolia } from "thirdweb/chains";
4849
4950
const wallets = [
5051
inAppWallet(
51-
// turn on gas sponsorship for in-app wallets
52-
{ smartAccount: { chain, sponsorGas: true } },
53-
),
54-
];
52+
{
53+
// turn on gas sponsorship for in-app wallets
54+
// Can use EIP4337 or EIP7702 on supported chains
55+
executionMode: {
56+
mode: "EIP4337",
57+
smartAccount: { chain: baseSepolia, sponsorGas: true },
58+
},
59+
}),
60+
];
5561
5662
function App() {
5763
return (

apps/playground-web/src/app/navLinks.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,15 @@ const staticSidebarLinks: SidebarLink[] = [
5151
href: "/connect/account-abstraction/connect",
5252
},
5353
{
54-
name: "Sponsor Gas",
54+
name: "EIP-4337",
5555
href: "/connect/account-abstraction/sponsor",
5656
},
5757
{
58-
name: "Native AA (zkSync)",
58+
name: "EIP-7702",
59+
href: "/connect/account-abstraction/7702",
60+
},
61+
{
62+
name: "Native (zkSync)",
5963
href: "/connect/account-abstraction/native-aa",
6064
},
6165
],
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { getContract } from "thirdweb";
5+
import { sepolia } from "thirdweb/chains";
6+
import { claimTo } from "thirdweb/extensions/erc1155";
7+
import { getNFT, getOwnedNFTs } from "thirdweb/extensions/erc1155";
8+
import {
9+
ConnectButton,
10+
MediaRenderer,
11+
TransactionButton,
12+
useActiveAccount,
13+
useReadContract,
14+
} from "thirdweb/react";
15+
import { shortenHex } from "thirdweb/utils";
16+
import { inAppWallet } from "thirdweb/wallets/in-app";
17+
import { THIRDWEB_CLIENT } from "../../lib/client";
18+
19+
const chain = sepolia;
20+
const editionDropAddress = "0x7B3e0B8353Ad5cD6C60355B50550F63335752f9F";
21+
const editionDropTokenId = 1n;
22+
23+
const editionDropContract = getContract({
24+
address: editionDropAddress,
25+
chain,
26+
client: THIRDWEB_CLIENT,
27+
});
28+
29+
const iaw = inAppWallet({
30+
executionMode: {
31+
mode: "EIP7702",
32+
sponsorGas: true,
33+
},
34+
});
35+
36+
export function Eip7702SmartAccountPreview() {
37+
const [txHash, setTxHash] = useState<string | null>(null);
38+
const activeEOA = useActiveAccount();
39+
const { data: nft, isLoading: isNftLoading } = useReadContract(getNFT, {
40+
contract: editionDropContract,
41+
tokenId: editionDropTokenId,
42+
});
43+
const { data: ownedNfts } = useReadContract(getOwnedNFTs, {
44+
contract: editionDropContract,
45+
useIndexer: false,
46+
// biome-ignore lint/style/noNonNullAssertion: handled by queryOptions
47+
address: activeEOA?.address!,
48+
queryOptions: { enabled: !!activeEOA },
49+
});
50+
51+
return (
52+
<div className="flex flex-col items-center justify-center gap-4">
53+
{isNftLoading ? (
54+
<div className="mt-24 w-full">Loading...</div>
55+
) : (
56+
<>
57+
<div className="flex flex-col justify-center gap-2 p-2">
58+
<ConnectButton
59+
client={THIRDWEB_CLIENT}
60+
chain={sepolia}
61+
wallets={[iaw]}
62+
connectButton={{
63+
label: "Login to mint!",
64+
}}
65+
/>
66+
</div>
67+
{nft ? (
68+
<MediaRenderer
69+
client={THIRDWEB_CLIENT}
70+
src={nft.metadata.image}
71+
style={{ width: "300px", marginTop: "10px" }}
72+
/>
73+
) : null}
74+
{activeEOA ? (
75+
<div className="flex flex-col justify-center gap-4 p-2">
76+
<p className="mb-2 text-center font-semibold">
77+
You own {ownedNfts?.[0]?.quantityOwned.toString() || "0"}{" "}
78+
{nft?.metadata?.name}
79+
</p>
80+
<TransactionButton
81+
transaction={() =>
82+
claimTo({
83+
contract: editionDropContract,
84+
tokenId: editionDropTokenId,
85+
to: activeEOA.address,
86+
quantity: 1n,
87+
})
88+
}
89+
payModal={{
90+
metadata: nft?.metadata,
91+
}}
92+
onError={(error) => {
93+
alert(`Error: ${error.message}`);
94+
}}
95+
onClick={() => {
96+
setTxHash(null);
97+
}}
98+
onTransactionSent={async (tx) => {
99+
setTxHash(tx.transactionHash);
100+
}}
101+
>
102+
Mint with EIP-7702
103+
</TransactionButton>
104+
</div>
105+
) : null}
106+
{txHash ? (
107+
<div className="flex flex-col justify-center p-2">
108+
<p className="mb-2 text-center text-green-500">
109+
Minted! Tx Hash:{" "}
110+
<a
111+
href={`${chain.blockExplorers?.[0]?.url}/tx/${txHash}`}
112+
target="_blank"
113+
rel="noopener noreferrer"
114+
className="underline"
115+
>
116+
{shortenHex(txHash)}
117+
</a>
118+
</p>
119+
</div>
120+
) : null}
121+
</>
122+
)}
123+
</div>
124+
);
125+
}

apps/playground-web/src/components/account-abstraction/sponsored-tx.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ export function SponsoredTxPreview() {
3636
contract: editionDropContract,
3737
// biome-ignore lint/style/noNonNullAssertion: handled by queryOptions
3838
address: smartAccount?.address!,
39+
useIndexer: false,
3940
queryOptions: { enabled: !!smartAccount },
4041
});
4142

4243
return (
43-
<div className="flex flex-col items-center justify-center">
44+
<div className="flex flex-col items-center justify-center gap-4">
4445
{isNftLoading ? (
4546
<div className="mt-24 w-full">Loading...</div>
4647
) : (
@@ -63,14 +64,14 @@ export function SponsoredTxPreview() {
6364
<MediaRenderer
6465
client={THIRDWEB_CLIENT}
6566
src={nft.metadata.image}
66-
style={{ width: "400px", marginTop: "10px" }}
67+
style={{ width: "300px", marginTop: "10px" }}
6768
/>
6869
) : null}
6970
{smartAccount ? (
7071
<div className="flex flex-col justify-center p-2">
7172
<p className="mb-2 text-center font-semibold">
7273
You own {ownedNfts?.[0]?.quantityOwned.toString() || "0"}{" "}
73-
Kittens
74+
{nft?.metadata?.name}
7475
</p>
7576
<TransactionButton
7677
transaction={() =>
@@ -94,7 +95,7 @@ export function SponsoredTxPreview() {
9495
setTxHash(receipt.transactionHash);
9596
}}
9697
>
97-
Mint
98+
Mint with EIP-4337
9899
</TransactionButton>
99100
</div>
100101
) : null}

apps/playground-web/src/components/in-app-wallet/sponsored-tx.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,13 @@ export function SponsoredInAppTxPreview() {
4444
"guest",
4545
],
4646
},
47-
smartAccount: {
48-
chain: baseSepolia,
49-
sponsorGas: true,
47+
// TODO (7702): update to 7702 once pectra is out
48+
executionMode: {
49+
mode: "EIP4337",
50+
smartAccount: {
51+
chain: baseSepolia,
52+
sponsorGas: true,
53+
},
5054
},
5155
}),
5256
]}

packages/thirdweb/.size-limit.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
{
99
"name": "thirdweb (cjs)",
1010
"path": "./dist/cjs/exports/thirdweb.js",
11-
"limit": "150 kB"
11+
"limit": "200 kB"
1212
},
1313
{
1414
"name": "thirdweb (minimal + tree-shaking)",

0 commit comments

Comments
 (0)