From 8fb81be5dfec1915d3607b9c0ae24f103afc0064 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 25 Jul 2024 12:01:26 +0200 Subject: [PATCH 1/3] fix(devnet): wait 1s because opening web page demo --- scripts/start-demo.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/start-demo.sh b/scripts/start-demo.sh index 46284375..221618ae 100755 --- a/scripts/start-demo.sh +++ b/scripts/start-demo.sh @@ -4,8 +4,8 @@ echo "Starting the web demo." # Define the commands as an array commands=( - "cd bolt-web-demo/frontend && yarn && yarn dev" - "cd bolt-web-demo/backend && yarn && yarn dev" + "cd bolt-web-demo/frontend && yarn && yarn dev" + "cd bolt-web-demo/backend && yarn && yarn dev" ) # Function to quit all processes on Ctrl+C @@ -13,9 +13,9 @@ quit_all() { echo "Caught SIGINT, quitting all processes." pids=($(jobs -p)) for pid in "${pids[@]}"; do - kill "$pid" # Ensure to kill each child process + kill "$pid" # Ensure to kill each child process done - wait # Wait for all processes to exit before script exits + wait # Wait for all processes to exit before script exits exit } @@ -25,10 +25,12 @@ trap 'quit_all' SIGINT # Start the commands in the background for command in "${commands[@]}"; do echo "Starting: $command" - eval "$command" & # Use eval to handle complex commands with CD and chaining + eval "$command" & # Use eval to handle complex commands with CD and chaining done -# Open the browser +sleep 1 + +# Open the browser if [ "$(uname)" = "Darwin" ]; then open "http://localhost:3000" elif [ "$(expr substr $(uname -s) 1 5)" = "Linux" ]; then From 4d3c06e9ea990622b8d81c06869255f546562075 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 25 Jul 2024 12:01:48 +0200 Subject: [PATCH 2/3] chore(demo): constants --- bolt-web-demo/frontend/src/lib/constants.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 bolt-web-demo/frontend/src/lib/constants.ts diff --git a/bolt-web-demo/frontend/src/lib/constants.ts b/bolt-web-demo/frontend/src/lib/constants.ts new file mode 100644 index 00000000..12ec9c96 --- /dev/null +++ b/bolt-web-demo/frontend/src/lib/constants.ts @@ -0,0 +1,5 @@ +// Test private key, for which address[0] holds 1000 ETH in the Kurtosis devnet +export const PRIVATE_KEY = + "39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d"; +export const KURTOSIS_CHAIN_ID = 3151908; +export const SERVER_URL = "http://localhost:3001"; From 2f83d394c95bce61abce2e9525a27bc3001c0802 Mon Sep 17 00:00:00 2001 From: thedevbirb Date: Thu, 25 Jul 2024 12:02:08 +0200 Subject: [PATCH 3/3] fix(demo): handle multiple preconf requests nonce --- bolt-web-demo/frontend/src/app/page.tsx | 60 ++++++++++++++++++++---- bolt-web-demo/frontend/src/lib/wallet.ts | 21 +++------ 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/bolt-web-demo/frontend/src/app/page.tsx b/bolt-web-demo/frontend/src/app/page.tsx index fc339a1c..d1a2903c 100644 --- a/bolt-web-demo/frontend/src/app/page.tsx +++ b/bolt-web-demo/frontend/src/app/page.tsx @@ -2,12 +2,14 @@ import io from "socket.io-client"; import Image from "next/image"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useMemo, useCallback } from "react"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Button } from "@/components/ui/button"; import { createPreconfPayload } from "@/lib/wallet"; import { EventType } from "@/lib/types"; import { Progress } from "@/components/ui/progress"; +import { ethers } from "ethers"; +import { PRIVATE_KEY, SERVER_URL } from "@/lib/constants"; type Event = { message: string; @@ -16,8 +18,6 @@ type Event = { link?: string; }; -export const SERVER_URL = "http://localhost:3001"; - export default function Home() { const [events, setEvents] = useState>([]); @@ -37,6 +37,16 @@ export default function Home() { const [beaconClientUrl, setBeaconClientUrl] = useState(""); const [providerUrl, setProviderUrl] = useState(""); const [explorerUrl, setExplorerUrl] = useState(""); + const [preconfirmationRequests, setPreconfirmationRequests] = useState< + Array<{ slot: number; count: number }> + >([]); + const [nonce, setNonce] = useState(0); + + const wallet = useMemo(() => { + const provider = new ethers.JsonRpcProvider(providerUrl); + const wallet = new ethers.Wallet(PRIVATE_KEY, provider); + return wallet; + }, [providerUrl]); useEffect(() => { fetch(`${SERVER_URL}/retry-port-events`); @@ -49,10 +59,11 @@ export default function Home() { const newSocket = io(SERVER_URL, { autoConnect: true }); newSocket.on("new-event", (event: Event) => { - console.info("Event from server:", event); + // console.info("Event from server:", event); if (event.type === EventType.NEW_SLOT) { - if (Number(event.message) === preconfSlot + 64) { + const slot = Number(event.message); + if (slot === preconfSlot + 64) { setPreconfFinalized(true); setFinalizationTimerActive(false); dispatchEvent({ @@ -60,6 +71,14 @@ export default function Home() { timestamp: new Date().toISOString(), }); } + + // Drop old requests + setPreconfirmationRequests((prev) => + prev.filter((req) => req.slot >= slot), + ); + + // Update the nonce + wallet.getNonce().then((nonce) => setNonce(nonce)); } // If the event has a special type, handle it differently @@ -105,7 +124,7 @@ export default function Home() { return () => { newSocket.close(); }; - }, [explorerUrl, preconfSlot]); + }, [explorerUrl, preconfSlot, wallet]); useEffect(() => { let interval: any = null; @@ -149,7 +168,7 @@ export default function Home() { return () => clearInterval(interval); }, [finalizationTimerActive]); - async function sendPreconfirmation() { + const sendPreconfirmation = useCallback(async () => { // Reset state setEvents([]); setPreconfSent(true); @@ -160,10 +179,31 @@ export default function Home() { setFinalizationTime(0); try { - const { payload, txHash } = await createPreconfPayload(providerUrl); + const nonceWithPreconfs = + nonce + + preconfirmationRequests + .map((req) => req.count) + .reduce((acc, c) => acc + c, 0); + + const { payload, txHash } = await createPreconfPayload( + wallet, + nonceWithPreconfs, + ); + + setPreconfirmationRequests((prev) => { + for (let i = 0; i < prev.length; i++) { + if (prev[i].slot === payload.slot) { + prev[i] = { ...prev[i], count: prev[i].count + 1 }; + return [...prev]; + } + } + prev.push({ slot: payload.slot, count: 1 }); + return [...prev]; + }); + setPreconfSlot(payload.slot); dispatchEvent({ - message: `Preconfirmation request sent for tx: ${txHash} at slot ${payload.slot}`, + message: `Preconfirmation request sent for tx: ${txHash} at slot ${payload.slot} with nonce ${nonceWithPreconfs}`, timestamp: new Date().toISOString(), }); @@ -185,7 +225,7 @@ export default function Home() { } catch (e) { console.error(e); } - } + }, [preconfirmationRequests, nonce, wallet]); function dispatchEvent(event: Event) { setEvents((prev) => [event, ...prev]); diff --git a/bolt-web-demo/frontend/src/lib/wallet.ts b/bolt-web-demo/frontend/src/lib/wallet.ts index 4a7a63a8..4235c855 100644 --- a/bolt-web-demo/frontend/src/lib/wallet.ts +++ b/bolt-web-demo/frontend/src/lib/wallet.ts @@ -1,10 +1,6 @@ -import { SERVER_URL } from "@/app/page"; import { TransactionRequest, keccak256 } from "ethers"; import { ethers } from "ethers"; - -// Test private key, for which address[0] holds 1000 ETH in the Kurtosis devnet -const PRIVATE_KEY = - "39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d"; +import { KURTOSIS_CHAIN_ID, SERVER_URL } from "./constants"; type InclusionRequestPayload = { slot: number; @@ -13,16 +9,13 @@ type InclusionRequestPayload = { }; export async function createPreconfPayload( - providerUrl: string + wallet: ethers.Wallet, + nonce: number, ): Promise<{ payload: InclusionRequestPayload; txHash: string }> { - // Create a Wallet instance from a private key - const provider = new ethers.JsonRpcProvider(providerUrl); - const wallet = new ethers.Wallet(PRIVATE_KEY, provider); - // Define the transaction const tx: TransactionRequest = { - chainId: (await provider.getNetwork()).chainId, - nonce: await wallet.getNonce(), + chainId: KURTOSIS_CHAIN_ID, + nonce: nonce, from: await wallet.getAddress(), to: "0xdeaDDeADDEaDdeaDdEAddEADDEAdDeadDEADDEaD", value: ethers.parseEther("0.0069420"), @@ -39,8 +32,6 @@ export async function createPreconfPayload( const txHash = keccak256(signedTx); const slot = (await getLatestSlot()) + 2; - console.log("preconf target slot: ", slot); - // Create a signature over the request fields "slot" and "tx" using the same signer // to authenticate the preconfirmation request through bolt. const slotBytes = numberToLittleEndianBytes(slot); @@ -57,7 +48,7 @@ export async function createPreconfPayload( export async function getLatestSlot(): Promise { const slotResponse = await fetch(`${SERVER_URL}/latest-slot`).then( - (response) => response.json() + (response) => response.json(), ); return Number(slotResponse.slot); }