Skip to content

Commit

Permalink
Om update (#259)
Browse files Browse the repository at this point in the history
* feat(): added mutation and hooks

* support erc7412

* fix(NodeStateButton)

* feat(queries): increased staletime

* feat(support-non-erc7412)

* fix(deps)
  • Loading branch information
fritzschoff authored May 3, 2024
1 parent e77163c commit e2b8877
Show file tree
Hide file tree
Showing 16 changed files with 234 additions and 285 deletions.
5 changes: 3 additions & 2 deletions oracle-manager/ui/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
NEXT_PUBLIC_WC_PROJECT_ID=xxx
NEXT_PUBLIC_INFURA_KEY=xxx
INFURA_KEY=xxx
PYTH_MAINNET_ENDPOINT=https://hermes.pyth.network/
PYTH_TESTNET_ENDPOINT=https://hermes.pyth.network/
12 changes: 6 additions & 6 deletions oracle-manager/ui/components/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Text, useDisclosure, useToast } from '@chakra-ui/react';
import { FC, useEffect, useState } from 'react';
import { FC, useEffect, useMemo, useState } from 'react';
import ReactFlow, {
Background,
useEdgesState,
Expand Down Expand Up @@ -42,7 +42,8 @@ const NODE_TYPES = {

export const Chart: FC<{ cannotRemoveEdges?: boolean }> = ({ cannotRemoveEdges }) => {
const toast = useToast();
const params = new URLSearchParams(window.location.search);
const location = useLocation();
const queryParams = useMemo(() => new URLSearchParams(location.search), [location.search]);
const { pathname } = useLocation();
const { isOpen, onOpen, onClose } = useDisclosure();
const [nodes, setNodes] = useRecoilState(nodesState);
Expand Down Expand Up @@ -114,15 +115,15 @@ export const Chart: FC<{ cannotRemoveEdges?: boolean }> = ({ cannotRemoveEdges }
};

useEffect(() => {
if (!!params.values().next().value) {
if (!!queryParams.values().next().value) {
const stateObject: Partial<{
tid: string;
id: string;
loc: string;
pam: string;
par: string;
net: string;
}> = Object.fromEntries(params.entries());
}> = Object.fromEntries(queryParams.entries());
const typeIds: number[] = JSON.parse(stateObject?.tid || '[]');
const ids: string[] = JSON.parse(stateObject?.id || '[]');
const locations: { x: string; y: string }[] = JSON.parse(stateObject?.loc || '[]');
Expand Down Expand Up @@ -177,8 +178,7 @@ export const Chart: FC<{ cannotRemoveEdges?: boolean }> = ({ cannotRemoveEdges }
}
});
}
// eslint-disable-next-line
}, []);
}, [edges, nodes, setEdges, setNodes, queryParams]);

return (
<Box
Expand Down
169 changes: 32 additions & 137 deletions oracle-manager/ui/components/NodeStateButton.tsx
Original file line number Diff line number Diff line change
@@ -1,153 +1,44 @@
import { Button, Flex, Spinner, Text, useToast } from '@chakra-ui/react';
import { providers, utils } from 'ethers';
import { FC, useCallback, useEffect, useState } from 'react';
import { useConnectWallet } from '@web3-onboard/react';
import { encodeBytesByNodeType, getNodeModuleContract, hashId } from '../utils/contracts';
import { providers } from 'ethers';
import { FC, useCallback, useState } from 'react';
import { encodeBytesByNodeType, getNodeModuleContract } from '../utils/contracts';
import { Node } from '../utils/types';
import { useRecoilState } from 'recoil';
import { nodesState } from '../state/nodes';
import { shortAddress } from '../utils/addresses';
import { NETWORKS, useIsConnected, useNetwork, useSigner } from '@snx-v3/useBlockchain';
import { useNetwork, useSigner } from '@snx-v3/useBlockchain';
import { useParams } from 'react-router-dom';

let interval: any;
import { useFetchPrice } from '../hooks/useFetchPrice';
import { findParentNode } from '../utils/nodes';

export const NodeStateButton: FC<{ node: Node }> = ({ node }) => {
const [nodes, setNodes] = useRecoilState(nodesState);
const [isLoading, setIsLoading] = useState(false);
const [nodeState, setNodeState] = useState<'registerNode' | 'nodeRegistered'>('registerNode');
const [nodeId, setNodeId] = useState('');
const [price, setPrice] = useState('0');
const [time, setTime] = useState(new Date());
const [_, connect] = useConnectWallet();
const signer = useSigner();
const isWalletConnected = useIsConnected();
const { network, setNetwork } = useNetwork();

const { network } = useNetwork();
const toast = useToast();
const param = useParams();

const networkParam = param?.network ? Number(param.network) : undefined;
const provider = new providers.JsonRpcProvider(
NETWORKS.filter((network) => network.id === (networkParam || 1))[0].rpcUrl()
);
const findParentNode = useCallback(
(parentId: string) => {
const parentNode = nodes.find((node) => node.id === parentId);
if (parentNode) {
return hashId(parentNode, []);
}
return '';
},
[nodes]
);

useEffect(() => {
const fetchNodeState = async () => {
if (network?.id) {
try {
const contract = await getNodeModuleContract(networkParam ? provider : signer!, network);
const hashedId = hashId(node, node.parents.map(findParentNode));
const nodeFromChain = await contract.getNode(hashedId);
if (nodeFromChain[0] > 0) {
const nodeID = await contract.getNodeId(
nodeFromChain[0],
nodeFromChain[1],
nodeFromChain[2]
);
setNodeId(nodeID);
setNodeState('nodeRegistered');
setNodes((state) => {
return state.map((n) => {
if (n.id === nodeID) {
return { ...n, isRegistered: true, network: network.id };
}
return n;
});
});
const price = await contract.process(nodeID);
setPrice(utils.formatEther(price[0]));
setTime(() => {
const newDate = new Date(1970, 0, 1);
newDate.setSeconds(price[1].toNumber());
return newDate;
});
interval = setInterval(async () => {
try {
const price = await contract.process(nodeID);
setTime(() => {
const newDate = new Date(1970, 0, 1);
newDate.setSeconds(price[1].toNumber());
return newDate;
});
setPrice(utils.formatEther(price[0]));
} catch (error) {
console.error('interval for price fetching errored ', error);
}
}, 10000);
} else {
setNodeState('registerNode');
setPrice('');
setTime(new Date());
setNodeId('');
clearInterval(interval);
setNodes((state) => {
return state.map((n) => {
if (n.id === node.id) {
return { ...n, isRegistered: false };
}
return n;
});
});
}
} catch (error) {
console.error(error);
setNodeState('registerNode');
setPrice('');
setTime(new Date());
setNodeId('');
clearInterval(interval);
setNodes((state) => {
return state.map((n) => {
if (n.id === node.id) {
return { ...n, isRegistered: false };
}
return n;
});
});
}
}
};
fetchNodeState();

// eslint-disable-next-line
}, [isWalletConnected, network?.id, node.type, node.parameters, node.parents, node.isRegistered]);
const networkParam = param?.network ? Number(param.network) : 1;
const { data, status } = useFetchPrice(node.id, networkParam);

const handleButtonClick = async () => {
if (!isWalletConnected) {
await connect();
if (network && node?.network) {
setNetwork(node.network);
}
} else if (nodeState === 'registerNode') {
if (node.isRegistered) {
try {
setIsLoading(true);
if (network?.id && signer) {
const contract = await getNodeModuleContract(signer, network);
const tx: providers.TransactionResponse = await contract.registerNode(
node.typeId,
encodeBytesByNodeType(node.typeId, node.parameters),
node.parents.map(findParentNode)
node.parents.map((id) => findParentNode(id, nodes))
);
await tx.wait(1);
const nodeID = await contract.getNodeId(
node.typeId,
encodeBytesByNodeType(node.typeId, node.parameters),
node.parents.map(findParentNode)
node.parents.map((id) => findParentNode(id, nodes))
);
if (nodeID) {
setNodeId(nodeID);
setNodeState('nodeRegistered');
setNodes((state) => {
return state.map((n) => {
if (n.id === nodeID) {
Expand All @@ -167,17 +58,18 @@ export const NodeStateButton: FC<{ node: Node }> = ({ node }) => {
};

const renderText = useCallback(() => {
if (!isWalletConnected) return <Text>Please connect your wallet</Text>;
if (nodeState === 'registerNode') return <Text>Register Node</Text>;
if (nodeState === 'nodeRegistered') return '';
return 'Something went wrong';
}, [nodeState, isWalletConnected]);
if (!node.isRegistered) return <Text>Register Node</Text>;
return 'Loading...';
}, [node.isRegistered]);

if (status === 'error') return 'Something went wrong...';
if (!data) return 'Loading...';

return (
<Flex flexDir="column" alignItems="center">
{isLoading ? (
<Spinner colorScheme="cyan" />
) : nodeState !== 'nodeRegistered' ? (
) : !node.isRegistered ? (
<Button
border="1px solid white"
size="xs"
Expand All @@ -190,31 +82,34 @@ export const NodeStateButton: FC<{ node: Node }> = ({ node }) => {
>
{renderText()}
</Button>
) : price !== '0' && !!price ? (
) : (
<Flex gap="2" flexDir="column">
<Text fontWeight="bold" color="whiteAlpha.800" fontSize="xs">
Price:
</Text>
<Text fontSize="xs" color="whiteAlpha.800">
{price}
{data.price.toString()}
</Text>
<Text fontWeight="bold" color="whiteAlpha.800" fontSize="xs">
Timestamp:
</Text>
<Text fontSize="xs" color="whiteAlpha.800">
{time.toLocaleTimeString()} - {time.toLocaleDateString()}
Timestamp:{' '}
{data.timestamp.toLocaleString(undefined, {
day: '2-digit',
month: 'short',
year: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
})}
</Text>
</Flex>
) : (
'Something went wrong'
)}
<Text
fontSize="xx-small"
mt="2"
_hover={{ opacity: 0.5 }}
onClick={(e) => {
e.stopPropagation();
navigator.clipboard.writeText(nodeId);
navigator.clipboard.writeText(node.id);
toast({
description: 'Copy ID to clipboard',
status: 'success',
Expand All @@ -223,7 +118,7 @@ export const NodeStateButton: FC<{ node: Node }> = ({ node }) => {
});
}}
>
{!!nodeId && <Text>Node ID: {shortAddress(nodeId)}</Text>}
{!!node.id && <Text>Node ID: {shortAddress(node.id)}</Text>}
</Text>
</Flex>
);
Expand Down
36 changes: 36 additions & 0 deletions oracle-manager/ui/components/RegisterAllNodesButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Button, Spinner, Toast } from '@chakra-ui/react';
import { useIsConnected } from '@snx-v3/useBlockchain';
import { nodesState } from '../state/nodes';
import { useRecoilState } from 'recoil';
import { useRegisterAllNodes } from '../mutations/useRegisterAllNodes';

export function RegisterAllNodesButton() {
const isWalletConnected = useIsConnected();
const [nodes] = useRecoilState(nodesState);
const { mutateAsync, isPending } = useRegisterAllNodes();

if (isPending) return <Spinner alignSelf="center" mr="4" colorScheme="cyan" />;

return (
<Button
variant="outline"
colorScheme="gray"
mr="4"
isDisabled={!isWalletConnected || nodes.length === 0}
onClick={async () => {
if (nodes.every((node) => node.isRegistered)) {
Toast({
title: 'All nodes are already registered',
status: 'info',
duration: 9000,
isClosable: true,
});
} else {
await mutateAsync();
}
}}
>
Register All Nodes
</Button>
);
}
29 changes: 14 additions & 15 deletions oracle-manager/ui/hooks/useFetchPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { importOracleManagerProxy, OracleManagerProxyType } from '@synthetixio/v
import { Contract } from 'ethers';
import { Wei } from '@synthetixio/wei';

export function useFetchPrice(nodeId: string) {
const baseNetwork = useGetNetwork(`0x${Number(8453).toString(16)}`);
export function useFetchPrice(nodeId: string, networkId: number) {
const baseNetwork = useGetNetwork(`0x${Number(networkId).toString(16)}`);
const baseProvider = useProviderForChain(baseNetwork);

return useQuery({
refetchInterval: 15000,
retry: false,
staleTime: 99999,

enabled: !!baseNetwork && !!baseProvider,
queryKey: ['snx-price', !!baseProvider],
queryFn: async () => {
Expand All @@ -36,28 +39,24 @@ export function useFetchPrice(nodeId: string) {
baseProvider,
price,
(txs) => {
const node = OracleManagerProxy.interface.decodeFunctionResult(
'process',
Array.isArray(txs) ? txs[0] : txs
);
return {
price: new Wei(
OracleManagerProxy.interface.decodeFunctionResult('process', txs[0])[0].price
),
timestamp: new Date(
Number(
OracleManagerProxy.interface
.decodeFunctionResult('process', txs[0])[0]
.timestamp.mul(1000)
.toString()
)
),
price: new Wei(node.price),
timestamp: new Date(Number(node.timestamp.mul(1000).toString())),
};
},
'priceCall'
);
} catch (error) {
console.error(error);
return { price: new Wei(0), timestamp: new Date() };
throw error;
}
} else {
throw new Error('BaseProvider and BaseNetwork undefined');
}
return { price: new Wei(0), timestamp: new Date() };
},
});
}
8 changes: 0 additions & 8 deletions oracle-manager/ui/load-env.sh

This file was deleted.

Loading

0 comments on commit e2b8877

Please sign in to comment.