Skip to content

Commit

Permalink
Add useInterval hook and SendTransactionErrorType import
Browse files Browse the repository at this point in the history
  • Loading branch information
vectorisvector committed Dec 7, 2023
1 parent 2089b22 commit 48c0efd
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 48 deletions.
100 changes: 52 additions & 48 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import {
http,
isAddress,
parseEther,
SendTransactionErrorType,
stringToHex,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";

import Log from "@/components/Log";
import { ChainKey, inscriptionChains } from "@/config/chains";
import useInterval from "@/hooks/useInterval";
import { handleAddress, handleLog } from "@/utils/helper";

const example =
Expand All @@ -39,11 +41,54 @@ export default function Home() {
const [inscription, setInscription] = useState<string>("");
const [gas, setGas] = useState<number>(0);
const [running, setRunning] = useState<boolean>(false);
const [timer, setTimer] = useState<NodeJS.Timeout>();
const [intervalTime, setIntervalTime] = useState<number>(1000);
const [delay, setDelay] = useState<number>(1000);
const [logs, setLogs] = useState<string[]>([]);
const [successCount, setSuccessCount] = useState<number>(0);

const pushLog = useCallback((log: string, state?: string) => {
setLogs((logs) => [handleLog(log, state), ...logs]);
}, []);

useInterval(
async () => {
const client = createWalletClient({
chain,
transport: http(rpc),
});
const accounts = privateKeys.map((key) => privateKeyToAccount(key));
const results = await Promise.allSettled(
accounts.map((account) => {
return client.sendTransaction({
account,
to: radio === "meToMe" ? account.address : toAddress,
maxPriorityFeePerGas: parseEther(gas.toString(), "gwei"),
value: 0n,
data: stringToHex(inscription),
});
}),
);
results.forEach((result, index) => {
const address = handleAddress(accounts[index].address);
if (result.status === "fulfilled") {
pushLog(`${address} ${result.value}`, "success");
setSuccessCount((count) => count + 1);
}
if (result.status === "rejected") {
const e = result.reason as SendTransactionErrorType;
let msg = `${e.name as string}: `;
if (e.name === "TransactionExecutionError") {
msg = msg + e.details;
}
if (e.name == "Error") {
msg = msg + e.message;
}
setLogs((logs) => [handleLog(`${address} ${msg}`, "error"), ...logs]);
}
});
},
running ? delay : null,
);

const run = useCallback(() => {
if (privateKeys.length === 0) {
setLogs((logs) => [handleLog("没有私钥", "error"), ...logs]);
Expand All @@ -63,47 +108,8 @@ export default function Home() {
return;
}

const client = createWalletClient({
chain,
transport: http(rpc),
});

const accounts = privateKeys.map((key) => privateKeyToAccount(key));

const timer = setInterval(async () => {
for (const account of accounts) {
try {
const hash = await client.sendTransaction({
account,
to: radio === "meToMe" ? account.address : toAddress,
maxPriorityFeePerGas: parseEther(gas.toString(), "gwei"),
value: 0n,
data: stringToHex(inscription),
});
setLogs((logs) => [
handleLog(`${handleAddress(account.address)} ${hash}`, "success"),
...logs,
]);
setSuccessCount((count) => count + 1);
} catch (error) {
setLogs((logs) => [
handleLog(`${handleAddress(account.address)} error`, "error"),
...logs,
]);
}
}
}, intervalTime);
setTimer(timer);
}, [
chain,
gas,
inscription,
intervalTime,
privateKeys,
radio,
rpc,
toAddress,
]);
setRunning(true);
}, [inscription, privateKeys, radio, toAddress]);

return (
<div className=" flex flex-col gap-4">
Expand Down Expand Up @@ -235,15 +241,15 @@ export default function Home() {
</div>

<div className=" flex flex-col gap-2">
<span>间隔时间(选填,最低 100 ms:</span>
<span>每笔交易间隔时间 (选填, 最低 0 ms):</span>
<TextField
type="number"
size="small"
placeholder="默认 1000 ms"
placeholder="默认 0 ms"
disabled={running}
onChange={(e) => {
const num = Number(e.target.value);
!Number.isNaN(num) && num >= 100 && setIntervalTime(num);
!Number.isNaN(num) && num >= 0 && setDelay(num);
}}
/>
</div>
Expand All @@ -253,11 +259,9 @@ export default function Home() {
color={running ? "error" : "success"}
onClick={() => {
if (!running) {
setRunning(true);
run();
} else {
setRunning(false);
timer && clearInterval(timer);
}
}}
>
Expand Down
32 changes: 32 additions & 0 deletions src/hooks/useInterval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useRef } from "react";

import { sleep } from "@/utils/helper";

export default function useInterval(
callback: () => Promise<void>,
delay: number | null,
) {
const savedCallback = useRef(callback);

useEffect(() => {
savedCallback.current = callback;
}, [callback]);

// 设置和清除循环
useEffect(() => {
let isRunning = true;

async function tick() {
while (isRunning && delay !== null) {
await savedCallback.current();
await sleep(delay);
}
}

tick();

return () => {
isRunning = false;
};
}, [delay]);
}
9 changes: 9 additions & 0 deletions src/utils/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ export function stringToHex(str: string) {
let view = encoder.encode(str);
return uint8ToHex(view);
}

/**
* Suspends the execution of the current function for a specified time.
* @param ms - The number of milliseconds to sleep.
* @returns A promise that resolves after the specified time has elapsed.
*/
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

0 comments on commit 48c0efd

Please sign in to comment.