Skip to content

Commit

Permalink
feat: added network switching logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Polybius93 committed Dec 1, 2023
1 parent f6b4682 commit 58f2d04
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 59 deletions.
22 changes: 21 additions & 1 deletion src/app/components/my-vaults-small/my-vaults-small.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { useVaults } from "@hooks/use-vaults";

import { VaultsListGroupContainer } from "../vaults-list/components/vaults-list-group-container";
import { MyVaultsSmallLayout } from "./components/my-vaults-small.layout";
import { useContext, useEffect, useState } from "react";
import { BlockchainContext } from "../../providers/blockchain-context-provider";
import { useSelector } from "react-redux";
import { RootState } from "@store/index";

interface MyVaultsSmallProps {
address?: string;
Expand All @@ -16,6 +20,10 @@ export function MyVaultsSmall({
address,
}: MyVaultsSmallProps): React.JSX.Element {
const navigate = useNavigate();
const [isLoaded, setIsLoaded] = useState(false);
const { network } = useSelector((state: RootState) => state.account);
const blockchainContext = useContext(BlockchainContext);
const ethereum = blockchainContext?.ethereum;
const {
readyVaults,
fundingVaults,
Expand All @@ -24,10 +32,22 @@ export function MyVaultsSmall({
closedVaults,
} = useVaults();

useEffect(() => {
if (network && ethereum?.isLoaded) {
const fetchAddressData = async () => {
await ethereum?.getAllVaults();
await ethereum?.getDLCBTCBalance();
await ethereum?.getLockedBTCBalance();
setIsLoaded(true);
};
fetchAddressData();

Check warning on line 43 in src/app/components/my-vaults-small/my-vaults-small.tsx

View workflow job for this annotation

GitHub Actions / lint-eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
}, [network, ethereum?.isLoaded]);

Check warning on line 45 in src/app/components/my-vaults-small/my-vaults-small.tsx

View workflow job for this annotation

GitHub Actions / lint-eslint

React Hook useEffect has a missing dependency: 'ethereum'. Either include it or remove the dependency array

return (
<MyVaultsSmallLayout>
<VaultsList title={"My Vaults"} height={"545px"} isScrollable={!address}>
{address ? (
{address && isLoaded ? (
<>
<VaultsListGroupContainer label="Lock BTC" vaults={readyVaults} />
<VaultsListGroupContainer
Expand Down
97 changes: 64 additions & 33 deletions src/app/hooks/use-ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import { useSelector } from "react-redux";

import { customShiftValue } from "@common/utilities";
import { EthereumError } from "@models/error-types";
import { Network, ethereumNetworks } from "@models/network";
import {
Network,
addNetworkParams,
ethereumNetworks,
hexChainIDs,
} from "@models/network";
import { EthereumNetwork } from "@models/network";
import { RawVault, Vault, VaultState } from "@models/vault";
import { RootState, store } from "@store/index";
import { accountActions } from "@store/slices/account/account.actions";
Expand All @@ -19,13 +25,16 @@ export interface UseEthereumReturnType {
dlcManagerContract: Contract | undefined;
dlcBTCContract: Contract | undefined;
dlcBTCBalance: number | undefined;
getDLCBTCBalance: () => Promise<void>;
lockedBTCBalance: number | undefined;
getLockedBTCBalance: () => Promise<void>;
requestEthereumAccount: (network: Network) => Promise<void>;
getAllVaults: () => Promise<void>;
getVault: (vaultUUID: string, vaultState: VaultState) => Promise<void>;
setupVault: (btcDepositAmount: number) => Promise<void>;
closeVault: (vaultUUID: string) => Promise<void>;
recommendTokenToMetamask: () => Promise<boolean>;
isLoaded: boolean;
}

function throwEthereumError(message: string, error: any): void {
Expand All @@ -44,6 +53,8 @@ export function useEthereum(): UseEthereumReturnType {
const { fundedVaults } = useVaults();
const { address, network } = useSelector((state: RootState) => state.account);

const [isLoaded, setIsLoaded] = useState<boolean>(false);

const [protocolContract, setProtocolContract] = useState<
Contract | undefined
>(undefined);
Expand All @@ -65,38 +76,15 @@ export function useEthereum(): UseEthereumReturnType {
if (!address || !network) return;

if (!protocolContract && !dlcManagerContract && !dlcBTCContract) {
setupEthereumConfiguration(network);
const setupConfiguration = async () => {
setIsLoaded(false);
await setupEthereumConfiguration(network);
setIsLoaded(true);
};
setupConfiguration();

Check warning on line 84 in src/app/hooks/use-ethereum.ts

View workflow job for this annotation

GitHub Actions / lint-eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
}, [address, network, protocolContract, dlcManagerContract, dlcBTCContract]);

useEffect(() => {
if (!address || !network || !protocolContract) return;

getAllVaults();
}, [address, network, protocolContract]);

useEffect(() => {
if (!address || !protocolContract || !dlcManagerContract || !dlcBTCContract)
return;

const fetchBalance = async () => {
try {
await getDLCBTCBalance();
await getLockedBTCBalance();
} catch (error) {
throwEthereumError(`Could not fetch balance: `, error);
}
};

fetchBalance();
}, [
address,
fundedVaults,
protocolContract,
dlcManagerContract,
dlcBTCContract,
]);

function formatVault(vault: any): Vault {
return {
uuid: vault.uuid,
Expand All @@ -108,27 +96,67 @@ export function useEthereum(): UseEthereumReturnType {
};
}

async function addEthereumNetwork(
newEthereumNetwork: EthereumNetwork,
): Promise<void> {
const { ethereum } = window;
try {
await ethereum.request({
method: "wallet_addEthereumChain",
params: addNetworkParams[newEthereumNetwork],
});
} catch (error: any) {
throwEthereumError(`Could not add Ethereum network: `, error);
}
}

async function switchEthereumNetwork(
newEthereumNetwork: EthereumNetwork,
): Promise<void> {
const { ethereum } = window;
try {
await ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: hexChainIDs[newEthereumNetwork] }],
});
} catch (error: any) {
if (error.code === 4902) {
await addEthereumNetwork(newEthereumNetwork);
} else {
throwEthereumError(`Could not switch Ethereum network: `, error);
}
}
}

async function setupEthereumConfiguration(network: Network): Promise<void> {
const ethereumProvider = await getEthereumProvider(network);
if (!ethereumProvider) {
throw new EthereumError("Failed to get Ethereum provider");
}
const { walletNetworkChainID, signer } = ethereumProvider;
if (!walletNetworkChainID || !signer) {
throw new EthereumError(
"Failed to get wallet network chain ID or signer",
);
}
await getEthereumContracts(walletNetworkChainID, signer);
}

async function getEthereumProvider(network: Network) {
try {
const { ethereum } = window;
const browserProvider = new ethers.providers.Web3Provider(ethereum);
const browserProvider = new ethers.providers.Web3Provider(
ethereum,
"any",
);
const signer = browserProvider.getSigner();

const walletNetwork = await browserProvider.getNetwork();
const walletNetworkChainID = walletNetwork.chainId.toString();

if (walletNetworkChainID !== network?.id) {
alert(`Please connect to ${network?.name}`);
return;
await switchEthereumNetwork(network?.id);
window.location.reload();
}
return { walletNetworkChainID, signer };
} catch (error) {
Expand Down Expand Up @@ -368,12 +396,15 @@ export function useEthereum(): UseEthereumReturnType {
dlcManagerContract,
dlcBTCContract,
dlcBTCBalance,
getDLCBTCBalance,
lockedBTCBalance,
getLockedBTCBalance,
requestEthereumAccount,
getAllVaults,
getVault,
setupVault,
closeVault,
recommendTokenToMetamask,
isLoaded,
};
}
22 changes: 7 additions & 15 deletions src/app/store/slices/vault/vault.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,19 @@ import { EthereumNetwork } from "@models/network";
import { Vault, VaultState } from "@models/vault";
import { PayloadAction, createSlice } from "@reduxjs/toolkit";

interface Vaults {
1: Vault[];
5: Vault[];
6: Vault[];
195: Vault[];
}
interface VaultSliceState {
vaults: Vaults;
vaults: { [key in EthereumNetwork]: Vault[] };
status: string;
error: string | null;
}

const initialVaultsState: Vaults = {
1: [],
5: [],
6: [],
195: [],
};

const initialVaultState: VaultSliceState = {
vaults: initialVaultsState,
vaults: {
[EthereumNetwork.Mainnet]: [],
[EthereumNetwork.Goerli]: [],
[EthereumNetwork.Sepolia]: [],
[EthereumNetwork.X1Testnet]: [],
},
status: "idle",
error: null,
};
Expand Down
88 changes: 78 additions & 10 deletions src/shared/models/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,101 @@ export interface Network {
export enum EthereumNetwork {
Mainnet = "1",
Goerli = "5",
Sepolia = "6",
Sepolia = "11155111",
X1Testnet = "195",
}

const EthreumOKXTestnet: Network = {
name: "X1test",
const ethereumOKXTestnet: Network = {
name: "X1Test",
id: EthereumNetwork.X1Testnet,
};

const EthereumMainnet: Network = {
const ethereumMainnet: Network = {
name: "Mainnet",
id: EthereumNetwork.Mainnet,
};

const EthereumGoerli: Network = {
const ethereumGoerli: Network = {
name: "Goerli",
id: EthereumNetwork.Goerli,
};

const EthereumSepolia: Network = {
const ethereumSepolia: Network = {
name: "Sepolia",
id: EthereumNetwork.Sepolia,
};

export const ethereumNetworks: Network[] = [
EthereumMainnet,
EthereumGoerli,
EthereumSepolia,
EthreumOKXTestnet,
ethereumMainnet,
ethereumGoerli,
ethereumSepolia,
ethereumOKXTestnet,
];

export const hexChainIDs: { [key in EthereumNetwork]: string } = {
[EthereumNetwork.Mainnet]: "0x1",
[EthereumNetwork.Goerli]: "0x5",
[EthereumNetwork.Sepolia]: "0xAA36A7",
[EthereumNetwork.X1Testnet]: "0x3C",
};

export const addNetworkParams = {
[EthereumNetwork.X1Testnet]: [
{
chainId: "0xC3",
rpcUrls: ["https://testrpc.x1.tech", "https://x1testrpc.okx.com/"],
chainName: "X1 testnet",
nativeCurrency: {
name: "OKB",
symbol: "OKB",
decimals: 18,
},
blockExplorerUrls: ["https://www.oklink.com/x1-test"],
},
],
[EthereumNetwork.Sepolia]: [
{
chainId: "11155111",
rpcUrls: [
"https://ethereum-sepolia.publicnode.com/",
"https://sepolia.infura.io/v3/",
],
chainName: "Sepolia Testnet",
nativeCurrency: {
name: "SepoliaETH",
symbol: "SepoliaETH",
decimals: 18,
},
blockExplorerUrls: ["https://sepolia.etherscan.io/"],
},
],
[EthereumNetwork.Goerli]: [
{
chainId: "5",
rpcUrls: [
"https://ethereum-goerli.publicnode.com",
"https://goerli.infura.io/v3/",
],
chainName: "Goerli Testnet",
nativeCurrency: {
name: "GoerliETH",
symbol: "GoerliETH",
decimals: 18,
},
blockExplorerUrls: ["https://goerli.etherscan.io/"],
},
],
[EthereumNetwork.Mainnet]: [
{
chainId: "1",
rpcUrls: ["https://mainnet.infura.io/v3/"],
chainName: "Ethereum Mainnet",
nativeCurrency: {
name: "ETH",
symbol: "ETH",
decimals: 18,
},
blockExplorerUrls: ["https://etherscan.io/"],
},
],
};

0 comments on commit 58f2d04

Please sign in to comment.