Skip to content

Commit

Permalink
Merge pull request #110 from chainbound/fix/demo/multiple-preconfs
Browse files Browse the repository at this point in the history
Fix multiple preconfirmations for the same slot in demo
  • Loading branch information
merklefruit authored Jul 25, 2024
2 parents 391d433 + 2f83d39 commit fca6055
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 31 deletions.
60 changes: 50 additions & 10 deletions bolt-web-demo/frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -16,8 +18,6 @@ type Event = {
link?: string;
};

export const SERVER_URL = "http://localhost:3001";

export default function Home() {
const [events, setEvents] = useState<Array<Event>>([]);

Expand All @@ -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`);
Expand All @@ -49,17 +59,26 @@ 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({
message: `Preconfirmed transaction finalized at slot ${event.message}`,
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
Expand Down Expand Up @@ -105,7 +124,7 @@ export default function Home() {
return () => {
newSocket.close();
};
}, [explorerUrl, preconfSlot]);
}, [explorerUrl, preconfSlot, wallet]);

useEffect(() => {
let interval: any = null;
Expand Down Expand Up @@ -149,7 +168,7 @@ export default function Home() {
return () => clearInterval(interval);
}, [finalizationTimerActive]);

async function sendPreconfirmation() {
const sendPreconfirmation = useCallback(async () => {
// Reset state
setEvents([]);
setPreconfSent(true);
Expand All @@ -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(),
});

Expand All @@ -185,7 +225,7 @@ export default function Home() {
} catch (e) {
console.error(e);
}
}
}, [preconfirmationRequests, nonce, wallet]);

function dispatchEvent(event: Event) {
setEvents((prev) => [event, ...prev]);
Expand Down
5 changes: 5 additions & 0 deletions bolt-web-demo/frontend/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -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";
21 changes: 6 additions & 15 deletions bolt-web-demo/frontend/src/lib/wallet.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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"),
Expand All @@ -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);
Expand All @@ -57,7 +48,7 @@ export async function createPreconfPayload(

export async function getLatestSlot(): Promise<number> {
const slotResponse = await fetch(`${SERVER_URL}/latest-slot`).then(
(response) => response.json()
(response) => response.json(),
);
return Number(slotResponse.slot);
}
Expand Down
14 changes: 8 additions & 6 deletions scripts/start-demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ 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
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
}

Expand All @@ -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
Expand Down

0 comments on commit fca6055

Please sign in to comment.