-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Closed
Description
Started here
How to reproduce:
ContractA
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "hardhat/console.sol";
import "./ContractB.sol";
/**
* Contract A that calls Contract B's getError function
* @author BuidlGuidl
*/
contract ContractA {
ContractB public contractB;
constructor(address _contractBAddress) {
contractB = ContractB(_contractBAddress);
}
/**
* Function that executes ContractB's getError function
* This will propagate the B_error() from ContractB
*/
function getErrorFromB() public {
console.log("ContractA: About to call ContractB.getError()");
contractB.getError();
}
}
ContractB
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import "hardhat/console.sol";
/**
* Contract B that contains a function that emits an error
* @author BuidlGuidl
*/
contract ContractB {
// Custom error definition
error B_error();
/**
* Function that emits a random error B_error()
*/
function getError() public pure {
console.log("ContractB: About to emit B_error()");
revert B_error();
}
}
01_deploy_contracts_ab.ts
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { Contract } from "ethers";
/**
* Deploys ContractB and ContractA
* ContractB is deployed first, then ContractA is deployed with ContractB's address
*
* @param hre HardhatRuntimeEnvironment object.
*/
const deployContractsAB: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployer } = await hre.getNamedAccounts();
const { deploy } = hre.deployments;
// Deploy ContractB first
await deploy("ContractB", {
from: deployer,
args: [], // ContractB has no constructor arguments
log: true,
autoMine: true,
});
// Get the deployed ContractB to get its address
const contractB = await hre.ethers.getContract<Contract>("ContractB", deployer);
console.log("📦 ContractB deployed at:", await contractB.getAddress());
// Deploy ContractA with ContractB's address
await deploy("ContractA", {
from: deployer,
args: [await contractB.getAddress()], // Pass ContractB's address to ContractA constructor
log: true,
autoMine: true,
});
// Get the deployed ContractA
const contractA = await hre.ethers.getContract<Contract>("ContractA", deployer);
console.log("📦 ContractA deployed at:", await contractA.getAddress());
console.log("🔗 ContractA is connected to ContractB at:", await contractA.contractB());
};
export default deployContractsAB;
// Tags are useful if you have multiple deploy files and only want to run one of them.
// e.g. yarn deploy --tags ContractsAB
deployContractsAB.tags = ["ContractsAB"];
page.tsx
"use client";
import { useState } from "react";
import Link from "next/link";
import type { NextPage } from "next";
import { useAccount } from "wagmi";
import { BugAntIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import { Address } from "~~/components/scaffold-eth";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
const Home: NextPage = () => {
const { address: connectedAddress } = useAccount();
const [isLoading, setIsLoading] = useState(false);
const { writeContractAsync: writeContractAAsync } = useScaffoldWriteContract({
contractName: "ContractA",
});
const handleCallContractA = async () => {
setIsLoading(true);
try {
// Call the getErrorFromB function which will throw an error
await writeContractAAsync({
functionName: "getErrorFromB",
});
} catch (error: any) {
console.log(error);
} finally {
setIsLoading(false);
}
};
return (
<>
<div className="flex items-center flex-col grow pt-10">
<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-ETH 2</span>
</h1>
<div className="flex justify-center items-center space-x-2 flex-col">
<p className="my-2 font-medium">Connected Address:</p>
<Address address={connectedAddress} />
</div>
{/* ContractA Button Section */}
<div className="flex flex-col items-center mt-8 p-6 bg-base-200 rounded-lg">
<h2 className="text-xl font-bold mb-4">ContractA Function Call</h2>
<button
className={`btn btn-primary ${isLoading ? "loading" : ""}`}
onClick={handleCallContractA}
disabled={isLoading}
>
{isLoading ? "Calling..." : "Call getErrorFromB()"}
</button>
</div>
<p className="text-center text-lg mt-6">
Get started by editing{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
packages/nextjs/app/page.tsx
</code>
</p>
<p className="text-center text-lg">
Edit your smart contract{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
YourContract.sol
</code>{" "}
in{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
packages/hardhat/contracts
</code>
</p>
</div>
<div className="grow bg-base-300 w-full mt-16 px-8 py-12">
<div className="flex justify-center items-center gap-12 flex-col md:flex-row">
<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<BugAntIcon className="h-8 w-8 fill-secondary" />
<p>
Tinker with your smart contract using the{" "}
<Link href="/debug" passHref className="link">
Debug Contracts
</Link>{" "}
tab.
</p>
</div>
<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<MagnifyingGlassIcon className="h-8 w-8 fill-secondary" />
<p>
Explore your local transactions with the{" "}
<Link href="/blockexplorer" passHref className="link">
Block Explorer
</Link>{" "}
tab.
</p>
</div>
</div>
</div>
</div>
</>
);
};
export default Home;
- yarn chain, yarn deploy, yarn start
- click
getErrorFromB()
button frompage.tsx
. You'll get the error

Metadata
Metadata
Assignees
Labels
No labels