Initial commit with 🏗️ create-eth @ 2.0.4
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
||||
import { usePublicClient, useWalletClient } from "wagmi";
|
||||
import { PlusIcon } from "@heroicons/react/24/outline";
|
||||
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
|
||||
import { notification } from "~~/utils/scaffold-eth";
|
||||
|
||||
export const AddOracleButton = () => {
|
||||
const { data: walletClient } = useWalletClient();
|
||||
const publicClient = usePublicClient();
|
||||
|
||||
const { writeContractAsync: writeWhitelistOracle } = useScaffoldWriteContract({ contractName: "WhitelistOracle" });
|
||||
|
||||
const handleAddOracle = async () => {
|
||||
if (!walletClient || !publicClient) {
|
||||
notification.error("Please connect wallet and enter both oracle owner address and initial price");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Generate a new oracle address
|
||||
const privateKey = generatePrivateKey();
|
||||
const oracleAddress = privateKeyToAccount(privateKey).address;
|
||||
|
||||
// Add oracle to whitelist
|
||||
await writeWhitelistOracle({
|
||||
functionName: "addOracle",
|
||||
args: [oracleAddress],
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.log("Error adding oracle:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button className="btn btn-primary h-full btn-sm font-normal gap-1" onClick={handleAddOracle}>
|
||||
<PlusIcon className="h-4 w-4" />
|
||||
<span>Add Oracle Node</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
67
packages/nextjs/components/oracle/whitelist/WhitelistRow.tsx
Normal file
67
packages/nextjs/components/oracle/whitelist/WhitelistRow.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { useEffect } from "react";
|
||||
import { EditableCell } from "../EditableCell";
|
||||
import { Address } from "@scaffold-ui/components";
|
||||
import { formatEther } from "viem";
|
||||
import { useBlockNumber, useReadContract } from "wagmi";
|
||||
import { HighlightedCell } from "~~/components/oracle/HighlightedCell";
|
||||
import { TimeAgo } from "~~/components/oracle/TimeAgo";
|
||||
import { WhitelistRowProps } from "~~/components/oracle/types";
|
||||
import { useScaffoldReadContract, useSelectedNetwork } from "~~/hooks/scaffold-eth";
|
||||
import { SIMPLE_ORACLE_ABI } from "~~/utils/constants";
|
||||
import { getHighlightColorForPrice } from "~~/utils/helpers";
|
||||
|
||||
export const WhitelistRow = ({ address }: WhitelistRowProps) => {
|
||||
const selectedNetwork = useSelectedNetwork();
|
||||
|
||||
const { data, refetch } = useReadContract({
|
||||
address: address,
|
||||
abi: SIMPLE_ORACLE_ABI,
|
||||
functionName: "getPrice",
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
}) as { data: readonly [bigint, bigint] | undefined; refetch: () => void };
|
||||
|
||||
const { data: blockNumber } = useBlockNumber({
|
||||
watch: true,
|
||||
chainId: selectedNetwork.id,
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [blockNumber, refetch]);
|
||||
|
||||
const { data: medianPrice } = useScaffoldReadContract({
|
||||
contractName: "WhitelistOracle",
|
||||
functionName: "getPrice",
|
||||
watch: true,
|
||||
}) as { data: bigint | undefined };
|
||||
|
||||
const { data: staleWindow } = useScaffoldReadContract({
|
||||
contractName: "WhitelistOracle",
|
||||
functionName: "STALE_DATA_WINDOW",
|
||||
}) as { data: bigint | undefined };
|
||||
|
||||
const isNotReported = data !== undefined && data[0] === 0n && data[1] === 0n;
|
||||
const lastReportedPriceFormatted =
|
||||
data === undefined || isNotReported ? "Not reported" : Number(parseFloat(formatEther(data?.[0] ?? 0n)).toFixed(2));
|
||||
|
||||
return (
|
||||
<tr className={`table-fixed`}>
|
||||
<td>
|
||||
<Address address={address} size="sm" format="short" onlyEnsOrAddress={true} />
|
||||
</td>
|
||||
<EditableCell
|
||||
value={lastReportedPriceFormatted}
|
||||
address={address}
|
||||
highlightColor={getHighlightColorForPrice(data?.[0], medianPrice)}
|
||||
/>
|
||||
<HighlightedCell value={0} highlightColor={""}>
|
||||
<TimeAgo timestamp={data?.[1]} staleWindow={staleWindow} />
|
||||
</HighlightedCell>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
110
packages/nextjs/components/oracle/whitelist/WhitelistTable.tsx
Normal file
110
packages/nextjs/components/oracle/whitelist/WhitelistTable.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import TooltipInfo from "~~/components/TooltipInfo";
|
||||
import { AddOracleButton } from "~~/components/oracle/whitelist/AddOracleButton";
|
||||
import { WhitelistRow } from "~~/components/oracle/whitelist/WhitelistRow";
|
||||
import { useScaffoldEventHistory, useScaffoldReadContract } from "~~/hooks/scaffold-eth";
|
||||
|
||||
const LoadingRow = () => {
|
||||
return (
|
||||
<tr>
|
||||
<td className="animate-pulse">
|
||||
<div className="h-8 bg-secondary rounded w-32"></div>
|
||||
</td>
|
||||
<td className="animate-pulse">
|
||||
<div className="h-8 bg-secondary rounded w-20"></div>
|
||||
</td>
|
||||
<td className="animate-pulse">
|
||||
<div className="h-8 bg-secondary rounded w-24"></div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const NoNodesRow = () => {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan={3} className="text-center">
|
||||
No nodes found
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
export const WhitelistTable = () => {
|
||||
const { data: oraclesAdded, isLoading: isLoadingOraclesAdded } = useScaffoldEventHistory({
|
||||
contractName: "WhitelistOracle",
|
||||
eventName: "OracleAdded",
|
||||
watch: true,
|
||||
});
|
||||
|
||||
const { data: oraclesRemoved, isLoading: isLoadingOraclesRemoved } = useScaffoldEventHistory({
|
||||
contractName: "WhitelistOracle",
|
||||
eventName: "OracleRemoved",
|
||||
watch: true,
|
||||
});
|
||||
|
||||
const { data: activeOracleNodes } = useScaffoldReadContract({
|
||||
contractName: "WhitelistOracle",
|
||||
functionName: "getActiveOracleNodes",
|
||||
watch: true,
|
||||
});
|
||||
|
||||
const isLoading = isLoadingOraclesAdded || isLoadingOraclesRemoved;
|
||||
const oracleAddresses = oraclesAdded
|
||||
?.map((item, index) => ({
|
||||
address: item?.args?.oracleAddress as string,
|
||||
originalIndex: index,
|
||||
}))
|
||||
?.filter(item => !oraclesRemoved?.some(removedOracle => removedOracle?.args?.oracleAddress === item.address));
|
||||
|
||||
const tooltipText = `This table displays registered oracle nodes that provide price data to the system. Nodes are considered active if they've reported within the last 24 seconds. You can add a new oracle node by clicking the "Add Oracle Node" button or edit the price of an oracle node.`;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex gap-2 justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-xl font-bold">Oracle Nodes</h2>
|
||||
<span className="text-sm text-gray-500">
|
||||
<TooltipInfo infoText={tooltipText} className="tooltip-right" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<AddOracleButton />
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-base-100 rounded-lg p-4 relative">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Node Address</th>
|
||||
<th>
|
||||
<div className="flex items-center gap-1">
|
||||
Last Reported Price (USD)
|
||||
<TooltipInfo infoText="Color shows proximity to median price" />
|
||||
</div>
|
||||
</th>
|
||||
<th>Last Reported Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{isLoading ? (
|
||||
<LoadingRow />
|
||||
) : oracleAddresses?.length === 0 ? (
|
||||
<NoNodesRow />
|
||||
) : (
|
||||
oracleAddresses?.map(item => (
|
||||
<WhitelistRow
|
||||
key={item.address}
|
||||
index={item.originalIndex}
|
||||
address={item.address}
|
||||
isActive={activeOracleNodes?.includes(item.address) ?? false}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user