generated from RohanNero/scaffold-eth-2-dynamic-chains
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,344 additions
and
1,166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Delegatecall ABI | ||
const delegateAbi = [ | ||
{ | ||
inputs: [ | ||
{ internalType: "address", name: "y", type: "address" }, | ||
{ internalType: "uint256", name: "z", type: "uint256" }, | ||
], | ||
name: "delegate", | ||
outputs: [{ internalType: "uint256", name: "", type: "uint256" }], | ||
stateMutability: "nonpayable", | ||
type: "function", | ||
}, | ||
{ | ||
inputs: [], | ||
name: "x", | ||
outputs: [{ internalType: "uint256", name: "", type: "uint256" }], | ||
stateMutability: "view", | ||
type: "function", | ||
}, | ||
] as const; | ||
|
||
export default delegateAbi; |
103 changes: 103 additions & 0 deletions
103
packages/nextjs/components/EIPs/Delegatecall/DataDisplay.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import React, { useEffect, useState } from "react"; | ||
import delegateAbi from "../../../abi/eips/Delegatecall"; | ||
import { chainData } from "../../../utils/scaffold-eth/networks"; | ||
import { createPublicClient, custom, encodeFunctionData } from "viem"; | ||
import { useNetwork } from "wagmi"; | ||
|
||
interface DataDisplayProps { | ||
returnData: { | ||
simulated: string; | ||
executedHash: string; | ||
}; | ||
displayError: (errorMessage: string) => void; | ||
} | ||
|
||
const DataDisplay: React.FC<DataDisplayProps> = ({ returnData, displayError }) => { | ||
// Get current chain info | ||
const { chain } = useNetwork(); | ||
// Store X value | ||
const [xValue, setXValue] = useState(0); | ||
|
||
// Views value of X inside Delegatecall contract | ||
const getXValue = async () => { | ||
if (!window.ethereum) { | ||
console.log("Window.ethereum is undefined!"); | ||
displayError("Window.ethereum is undefined!"); | ||
return; | ||
} | ||
const publicClient = createPublicClient({ | ||
transport: custom(window.ethereum), | ||
}); | ||
if (publicClient == undefined) { | ||
displayError("publicClient is undefined!"); | ||
return; | ||
} | ||
const callData = encodeFunctionData({ | ||
abi: delegateAbi, | ||
functionName: "x", | ||
}); | ||
if (!chain || !chain?.id) { | ||
displayError("Chain is undefined!"); | ||
return; | ||
} | ||
const contract = chainData[chain?.id]?.delegatecall?.main; | ||
if (!contract) { | ||
displayError("Delegatecall contract is undefined, try another chain!"); | ||
return; | ||
} | ||
console.log("to:", contract); | ||
const value = await publicClient.call({ | ||
data: callData, | ||
to: contract, | ||
}); | ||
|
||
if (!value?.data) { | ||
displayError("X value is undefined!"); | ||
return; | ||
} | ||
console.log("x value:", value); | ||
console.log("value number:", parseInt(value?.data.toString())); | ||
setXValue(parseInt(value.data)); | ||
}; | ||
|
||
// Function to handle copying transaction hash to clipboard | ||
const copyHash = () => { | ||
navigator.clipboard | ||
.writeText(returnData.executedHash) | ||
.then(() => { | ||
console.log("Transaction Hash copied to clipboard!"); | ||
}) | ||
.catch(error => { | ||
console.error("Failed to copy transaction hash to clipboard: ", error); | ||
}); | ||
}; | ||
|
||
// Code for viewing Delegatecall's `x` value | ||
useEffect(() => { | ||
// console.log("chain:", chain.id); | ||
// console.log("Delegatecall adress:", chainData[chain.id].delegatecall.main); | ||
if (!chain) { | ||
return; | ||
} | ||
getXValue(); | ||
}, [chain, returnData.executedHash]); | ||
|
||
return ( | ||
<> | ||
{" "} | ||
<div className="mb-4 text-center text-2xl flex justify-center gap-4"> | ||
<div>{`Current X value: ${xValue}`}</div> | ||
{returnData.simulated !== "" && <div>{`Simulated value: ${returnData.simulated}`}</div>} | ||
</div> | ||
{returnData.executedHash !== "" && ( | ||
<div className="mb-4 text-center text-gray-400 text-2xl"> | ||
<button className="hover:text-gray-500" title="Click to copy!" onClick={copyHash}> | ||
{returnData.executedHash == "Loading..." ? returnData.executedHash : `Tx hash: ${returnData.executedHash}`} | ||
</button> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default DataDisplay; |
44 changes: 44 additions & 0 deletions
44
packages/nextjs/components/EIPs/Delegatecall/ExtraCodeBlocks.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import SyntaxHighlighter from "react-syntax-highlighter"; | ||
import { atomOneDark } from "react-syntax-highlighter/dist/cjs/styles/hljs"; | ||
|
||
// Code blocks | ||
const additionDisplayCode = `contract AdditionContract { | ||
uint public x; | ||
fallback() external payable { | ||
uint input = abi.decode(msg.data, (uint)); | ||
x = x + input; | ||
} | ||
}`; | ||
const subtractionDisplayCode = `contract SubtractionContract { | ||
uint public x; | ||
fallback() external payable { | ||
uint input = abi.decode(msg.data, (uint)); | ||
x = x - input; | ||
} | ||
}`; | ||
const multiplicationDisplayCode = `contract MultiplicationContract { | ||
uint public x; | ||
fallback() external payable { | ||
uint input = abi.decode(msg.data, (uint)); | ||
x = x * input; | ||
} | ||
}`; | ||
|
||
// This code displays all code blocks for EIP 7 - Delegatecall except for the main code block | ||
const ExtraCodeBlocks = () => { | ||
return ( | ||
<div className="mb-4 flex flex-row gap-5 justify-center"> | ||
<SyntaxHighlighter className="border rounded-2xl" language="typescript" style={atomOneDark}> | ||
{additionDisplayCode} | ||
</SyntaxHighlighter> | ||
<SyntaxHighlighter className="border rounded-2xl" language="typescript" style={atomOneDark}> | ||
{subtractionDisplayCode} | ||
</SyntaxHighlighter> | ||
<SyntaxHighlighter className="border rounded-2xl" language="typescript" style={atomOneDark}> | ||
{multiplicationDisplayCode} | ||
</SyntaxHighlighter> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ExtraCodeBlocks; |
213 changes: 213 additions & 0 deletions
213
packages/nextjs/components/EIPs/Delegatecall/InputForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import React, { ChangeEvent, FormEvent, useState } from "react"; | ||
import delegateAbi from "../../../abi/eips/Delegatecall"; | ||
import { chainData } from "../../../utils/scaffold-eth/networks"; | ||
import { createPublicClient, createWalletClient, custom, encodeFunctionData } from "viem"; | ||
import { useNetwork } from "wagmi"; | ||
|
||
interface ReturnDataType { | ||
simulated: string; | ||
executedHash: string; | ||
} | ||
|
||
interface DataDisplayProps { | ||
returnData: ReturnDataType; | ||
displayError: (errorMessage: string) => void; | ||
setReturnData: React.Dispatch<React.SetStateAction<ReturnDataType>>; | ||
} | ||
|
||
const InputForm: React.FC<DataDisplayProps> = ({ displayError, returnData, setReturnData }) => { | ||
// Get current chain info | ||
const { chain } = useNetwork(); | ||
|
||
const [formData, setFormData] = useState({ | ||
input: "", | ||
to: "add", | ||
}); | ||
|
||
// Handle the state of tx interaction, if false the transaction will actually be submitted | ||
const [simulateOnly, setSimulateOnly] = useState<boolean>(true); | ||
|
||
// Function to handle form input changes | ||
const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => { | ||
// Extract the input value from the event | ||
const { name, value } = e.target; | ||
|
||
// Check if the input for x value is a valid integer | ||
if (name == "input" && !/^\d*$/.test(value)) { | ||
console.log(`Invalid input character: ${value} `); | ||
return; | ||
} | ||
|
||
// Update the state only if it's a valid integer | ||
setFormData({ | ||
...formData, | ||
[name]: value, | ||
}); | ||
}; | ||
|
||
// Update simulateOnly state value depending on checkbox status | ||
const handleCheckboxChange = () => { | ||
// Toggle the state when the checkbox is clicked | ||
setSimulateOnly(!simulateOnly); | ||
console.log("simulate value:", simulateOnly); | ||
}; | ||
|
||
// Calls a contract with delegatecall when form is submitted | ||
const handleSubmit = async (e: FormEvent) => { | ||
e.preventDefault(); | ||
|
||
if (!chain || !chain?.id) { | ||
console.log("Chain id is undefined!"); | ||
displayError("Chain id is undefined!"); | ||
return; | ||
} | ||
if (!window.ethereum) { | ||
console.log("Window.ethereum is undefined!"); | ||
displayError("Window.ethereum is undefined!"); | ||
return; | ||
} | ||
const publicClient = createPublicClient({ | ||
transport: custom(window.ethereum), | ||
}); | ||
const walletClient = createWalletClient({ | ||
transport: custom(window.ethereum), | ||
chain: chain, | ||
}); | ||
if (!walletClient || !publicClient) { | ||
console.log("Client is undefined!"); | ||
displayError("Client is undefined!"); | ||
return; | ||
} | ||
const [address] = await walletClient.requestAddresses(); | ||
|
||
if (!formData?.to) { | ||
displayError("formData.to is undefined!"); | ||
return; | ||
} | ||
|
||
const childContract = formData.to as keyof { main: string; add: string; sub: string; mul: string; reset: string }; | ||
|
||
if (!childContract) { | ||
displayError("Child contract string is undefined!"); | ||
return; | ||
} | ||
|
||
// Get address using key e.g. add, sub, mul | ||
const toAddress = chainData[chain?.id]?.delegatecall?.[childContract]; | ||
const mainAddress = chainData[chain?.id]?.delegatecall?.main; | ||
|
||
if (!toAddress || !mainAddress) { | ||
displayError("Chain address is undefiend!"); | ||
return; | ||
} | ||
|
||
const callData = encodeFunctionData({ | ||
abi: delegateAbi, | ||
functionName: "delegate", | ||
args: [toAddress, BigInt(formData.input)], | ||
}); | ||
|
||
console.log("toAddress:", toAddress); | ||
console.log("input:", formData.input); | ||
console.log("calldata:", callData); | ||
|
||
// Simulate `delegate` tx | ||
const { result } = (await publicClient.simulateContract({ | ||
address: mainAddress, | ||
abi: delegateAbi, | ||
args: [toAddress, BigInt(formData.input)], | ||
functionName: "delegate", | ||
account: address, | ||
})) as { result: bigint }; | ||
|
||
console.log("result:", result.toString()); | ||
|
||
setReturnData(prevData => ({ | ||
...prevData, | ||
simulated: result.toString(), | ||
})); | ||
|
||
// If simulateOnly is false, execute the transaction | ||
if (simulateOnly == false) { | ||
// Call `delegate` | ||
try { | ||
setReturnData(() => ({ | ||
simulated: "", | ||
executedHash: "Loading...", | ||
})); | ||
const delegateHash = await walletClient.sendTransaction({ | ||
account: address, | ||
to: chainData[chain?.id]?.delegatecall?.main, | ||
data: callData, | ||
}); | ||
console.log("hash:", delegateHash); | ||
const transaction = await publicClient.waitForTransactionReceipt({ hash: delegateHash }); | ||
console.log("tx receipt:", transaction); | ||
setReturnData(prevData => ({ | ||
...prevData, | ||
executedHash: delegateHash, | ||
})); | ||
} catch (error: any) { | ||
displayError(error); | ||
setReturnData(prevData => ({ | ||
...prevData, | ||
executedHash: "", | ||
})); | ||
} | ||
} | ||
console.log("returnData:", returnData); | ||
}; | ||
|
||
return ( | ||
<div className="mb-4 text-center text-xl"> | ||
Input Form | ||
<form onSubmit={handleSubmit}> | ||
<input | ||
type="number" | ||
name="input" | ||
step="1" | ||
placeholder="Input value" | ||
className="border p-1.5 text-gray-500 focus:ring-0 rounded w-1/4 bg-gray-300 hover:bg-gray-400 hover:text-gray-300" | ||
value={formData.input} | ||
onChange={handleInputChange} | ||
/> | ||
<select | ||
name="to" | ||
className="border p-1.5 text-gray-500 focus:ring-0 rounded w-1/4 bg-gray-300 hover:bg-gray-400 hover:text-gray-300 ml-2" | ||
value={formData.to} | ||
onChange={handleInputChange} | ||
> | ||
<option value="add">Addition</option> | ||
<option value="sub">Subtraction</option> | ||
<option value="mul">Multiplication</option> | ||
<option value="reset">Reset X to 0</option> | ||
</select> | ||
<div className="flex justify-center gap-2"> | ||
<button | ||
type="submit" | ||
disabled={formData.input === ""} | ||
className="bg-purple-700 border rounded my-2 px-4 py-2 bg-gradient-to-r from-gray-400 via-gray-300 to-gray-400 hover:from-gray-200 hover:to-gray-200 text-gray-500 hover:shadow-lg hover:bg-green-300 w-[50%]" | ||
> | ||
{simulateOnly ? "Simulate" : "Execute"} | ||
</button> | ||
|
||
{/* Checkbox input */} | ||
<input | ||
className="bg-gray-500 mt-2" | ||
type="checkbox" | ||
id="myCheckbox" | ||
checked={!simulateOnly} | ||
onChange={handleCheckboxChange} | ||
style={{ width: "45px", height: "45px", filter: "grayscale(100%)" }} | ||
title="When checked transactions will actually be executed!" | ||
/> | ||
|
||
{/* Label for the checkbox */} | ||
<label className="ml-2" htmlFor="myCheckbox" /> | ||
</div> | ||
</form> | ||
</div> | ||
); | ||
}; | ||
|
||
export default InputForm; |
Oops, something went wrong.