Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a1fe5c2
added polygon amoy (polygon mumbai deprecated) in ~packages/shared/sr…
UMainLove May 13, 2025
a7f7213
Switched client account type from LightAccount to MultiOWnerModularA…
UMainLove May 17, 2025
f87776f
feature: added button to upgrade an EOA to a SCA using EIP-7702 (FRON…
UMainLove Jun 13, 2025
b59a70f
Merge pull request #1 from UMainLove/new_feature
UMainLove Jun 14, 2025
86322de
fix: correctly showing account type connector
UMainLove Jun 19, 2025
3722d0e
Update branding and improve account type messaging
UMainLove Jun 21, 2025
fd49fa4
Merge pull request #2 from UMainLove/new_feature
UMainLove Jun 21, 2025
e8554c8
feature: Added account upgrade (eip7702) functionality using external…
UMainLove Jul 1, 2025
93a8299
Merge pull request #3 from UMainLove/new_feature
UMainLove Jul 1, 2025
c3a3c57
added custom errors and messages when an EOA is upgraded to a Smart W…
UMainLove Jul 3, 2025
82bcdd0
Merge pull request #4 from UMainLove/new_feature
UMainLove Jul 3, 2025
fa750dc
fix: public client retrieves the connected address with correct method
UMainLove Jul 3, 2025
eb8ed4b
Merge pull request #5 from UMainLove/new_feature
UMainLove Jul 3, 2025
458c37f
feature: added EOA interactions with in-app smart contracts
UMainLove Jul 8, 2025
133fc05
Merge pull request #6 from UMainLove/new_feature
UMainLove Jul 8, 2025
551dd55
upgrade: replaced EOA direct calls with delegated userOps (eip7702) w…
UMainLove Jul 8, 2025
4943cb9
Merge pull request #7 from UMainLove/new_feature
UMainLove Jul 8, 2025
4371d65
feature: Added explicit Smart Contract Account deployment button
UMainLove Jul 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true

[*.{js,json,yml}]
charset = utf-8
indent_style = space
indent_size = 2
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ node_modules
# cli
dist

.env
.env

# Claude
CLAUDE.md
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# 🏗 Scaffold-Alchemy
# 🏗 Scaffold-Alchemy by UMainLove

## THIS FORK CONTAINS EIP-7702 SUPPORT WITH METAMASK WALLETS

Scaffold-Alchemy is a fork of the popular starter project [Scaffold-Eth 2](https://scaffoldeth.io/). It is everything you need to build dApps on Ethereum. You can get started immediately NextJS, TypeScript, Hardhat, AccountKit, Enhanced APIs and Subgraphs 🤩

Expand All @@ -16,34 +18,42 @@ Before you begin, you need to install the following tools:

To get started with Scaffold-Alchemy, follow the steps below:

1. Install the latest version of Scaffold-Alchemy
Install the latest version of Scaffold-Alchemy

```
```bash
npx create-web3-dapp
```

2. In a terminal, deploy the test contract:
In a terminal, deploy the test contract:

```
```bash
yarn deploy
```

This command deploys a test smart contract to a testnet. You can see the default testnet in `packages/hardhat/hardhat/config.ts`

3. In a second terminal, start your NextJS app:
In a second terminal, start your NextJS app:

```
```bash
yarn start
```

Visit your app on: `http://localhost:3000`. You can interact with your smart contract using the `Debug Contracts` page. You can tweak the app config in `packages/nextjs/scaffold.config.ts`.
Visit your app on: `http://localhost:56900`. You can interact with your smart contract using the `Debug Contracts` page. You can tweak the app config in `packages/nextjs/scaffold.config.ts`.

## Additional Features

- Added deployment button for Modular Smart Contract Accounts (erc4337 + erc6900)
- Added eip-7702 upgrade button for connected EOA (Tested with: Metamask Wallets on Ethereum Sepolia testnet, private key required)
- Smart Wallets (EOA + eip7702 + erc4337) interactions fully supported

## Documentation

Visit our [docs](https://docs.alchemy.com/docs/scaffold-alchemy) to learn all the technical details and guides of Scaffold-Alchemy.
Visit [docs](https://docs.alchemy.com/docs/scaffold-alchemy) to learn all the technical details and guides of Scaffold-Alchemy.

## Contributing to Scaffold-Alchemy
Visit [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) to learn more about this Ethereum Improvement Proposal.

We welcome contributions to Scaffold-Alchemy!
Visit [Implementation](https://www.alchemy.com/docs/wallets/react/using-7702) to learn how eip-7702 could be implemented using Alchemy.

## Contributing to Scaffold-Alchemy

Please see [CONTRIBUTING.MD](https://github.com/alchemyplatform/scaffold-alchemy/blob/main/CONTRIBUTING.md) for more information and guidelines for contributing to Scaffold-Alchemy.
11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@
},
"packageManager": "[email protected]",
"dependencies": {
"@aa-sdk/core": "^4.24.0",
"@account-kit/infra": "^4.24.0",
"@account-kit/smart-contracts": "^4.24.0",
"@aa-sdk/core": "^4.31.0",
"@account-kit/infra": "^4.31.0",
"@account-kit/plugingen": "4.31.0",
"@account-kit/smart-contracts": "^4.31.0",
"@account-kit/wallet-client": "0.1.0-alpha.9",
"viem": "2.28.4"
},
"devDependencies": {
"husky": "^9.1.6",
"lint-staged": "^15.2.10"
"lint-staged": "^15.2.10",
"typescript": "^5.8.3"
},
"engines": {
"node": ">=18.18.0"
Expand Down
6 changes: 3 additions & 3 deletions packages/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@
"typescript": "<5.6.0"
},
"dependencies": {
"@aa-sdk/core": "^4.12.0",
"@account-kit/infra": "^4.12.0",
"@account-kit/smart-contracts": "^4.12.0",
"@aa-sdk/core": "^4.31.0",
"@account-kit/infra": "^4.31.0",
"@account-kit/smart-contracts": "^4.31.0",
"@inquirer/password": "^4.0.2",
"@openzeppelin/contracts": "^5.0.2",
"@scaffold-alchemy/shared": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* eslint-disable prettier/prettier */

"use client";

import { useEffect, useState } from "react";
Expand Down Expand Up @@ -34,43 +36,103 @@ export const WriteOnlyFunctionForm = ({
contractAddress,
inheritedFrom,
}: WriteOnlyFunctionFormProps) => {
const { client } = useClient();
const { client, isSmartWallet } = useClient(); // isSmartWallet is part of feature_1_part_3

// Only use useSendUserOperation for Smart Accounts cases
const { sendUserOperationAsync, isSendingUserOperation } = useSendUserOperation({
client: client,
client: !isSmartWallet ? client : undefined, // client is not needed for Smart Wallets, 'client: client' is used for Smart Accounts
waitForTxn: true,
});
const [hash, setHash] = useState<Hex | undefined>();
const [form, setForm] = useState<Record<string, any>>(() => getInitialFormState(abiFunction));
const [txValue, setTxValue] = useState<string>("");
const [isSmartWalletSending, setIsSmartWalletSending] = useState(false); // feature_1_part_3: Track Smart Wallet sending state
const { chain } = useChain();
const writeTxn = useTransactor();
const { targetNetwork } = useTargetNetwork();
const writeDisabled = !chain || chain?.id !== targetNetwork.id;
const isSending = isSendingUserOperation || isSmartWalletSending; // feature_1_part_3: Track sending state

const handleWrite = async () => {
if (sendUserOperationAsync) {
try {
const makeWriteWithParams = async () => {
if (!client) throw Error("You must first login before making an onchain action");

// feature_1_part_3: Handle Smart Wallet sending block start -----
if (!client && !sendUserOperationAsync) {
console.error("No client available for transaction");
return;
}

try {
if (isSmartWallet) {
setIsSmartWalletSending(true);
}

const makeWriteWithParams = async (): Promise<Hex> => {
const encodedData = encodeFunctionData({
functionName: abiFunction.name,
abi: abi,
args: getParsedContractFunctionArgs(form),
});

let txHash: Hex;

// For Smart Wallets (EIP-7702), use the client directly
if (isSmartWallet && client) {
console.log("[WriteOnlyForm] Using Smart Wallet client for UserOp");
console.log("[WriteOnlyForm] Target:", contractAddress);
console.log("[WriteOnlyForm] Function:", abiFunction.name);
console.log("[WriteOnlyForm] Value:", BigInt(txValue || "0"));

try {
const result = await client.sendUserOperation({
uo: {
target: contractAddress,
data: encodedData,
value: BigInt(txValue || "0"),
},
});

console.log("[WriteOnlyForm] UserOp sent, result:", result);

// Wait for the transaction if we have a hash
if ('hash' in result && result.hash) {
console.log("[WriteOnlyForm] Waiting for UserOp transaction:", result.hash);
const confirmedHash = await client.waitForUserOperationTransaction({
hash: result.hash,
});
txHash = confirmedHash as Hex;
console.log("[WriteOnlyForm] Transaction confirmed:", txHash);
} else {
throw new Error("Failed to get transaction hash from Smart Wallet operation");
}
} catch (error) {
console.error("[WriteOnlyForm] Smart Wallet operation failed:", error);
throw error;
}
} else if (sendUserOperationAsync) {
// Use the SDK's sendUserOperationAsync for regular smart accounts
const { hash } = await sendUserOperationAsync({
uo: {
target: contractAddress,
data: encodeFunctionData({
functionName: abiFunction.name,
abi: abi,
args: getParsedContractFunctionArgs(form),
}),
value: BigInt(txValue),
data: encodedData,
value: BigInt(txValue || "0"),
},
});
setHash(hash);
return hash;
};
await writeTxn(makeWriteWithParams);
onChange();
} catch (e: any) {
console.error("⚡️ ~ file: WriteOnlyFunctionForm.tsx:handleWrite ~ error", e);
txHash = hash as Hex;
} else {
throw Error("No method available to send transaction");
}

setHash(txHash);
return txHash;
};

await writeTxn(makeWriteWithParams);
onChange();
} catch (e: any) {
console.error("⚡️ ~ file: WriteOnlyFunctionForm.tsx:handleWrite ~ error", e);
} finally {
if (isSmartWallet) {
setIsSmartWalletSending(false);
}
}
};
Expand All @@ -82,6 +144,7 @@ export const WriteOnlyFunctionForm = ({
useEffect(() => {
setDisplayedTxResult(txResult);
}, [txResult]);
// feature_1_part_3: Handle Smart Wallet sending block end -----

// TODO use `useMemo` to optimize also update in ReadOnlyFunctionForm
const transformedFunction = transformAbiFunction(abiFunction);
Expand Down Expand Up @@ -141,10 +204,10 @@ export const WriteOnlyFunctionForm = ({
>
<button
className="btn btn-secondary btn-sm"
disabled={writeDisabled || isSendingUserOperation}
disabled={writeDisabled || isSending || (!client && !sendUserOperationAsync)}
onClick={handleWrite}
>
{isSendingUserOperation && <span className="loading loading-spinner loading-xs"></span>}
{isSending && <span className="loading loading-spinner loading-xs"></span>}
Send 💸
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const Home: NextPage = () => {
<div className="px-5">
<h1 className="text-center">
<span className="block text-2xl mb-2">Welcome to</span>
<span className="block text-4xl font-bold">Scaffold-Alchemy</span>
<span className="block text-4xl font-bold">ID-Ephyrian (w/ Alchemy)</span>
</h1>
<div className="flex justify-center items-center space-x-2 flex-col sm:flex-row">
{isConnected ? (
Expand Down
Loading