bridge_ui: compile TVL hook
Change-Id: I706f37155d2e09312998aef9725226928eeeefb1
This commit is contained in:
parent
b641101158
commit
7e6123a3a8
|
@ -0,0 +1,188 @@
|
||||||
|
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
||||||
|
import { formatUnits } from "@ethersproject/units";
|
||||||
|
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||||
|
import {
|
||||||
|
AccountInfo,
|
||||||
|
Connection,
|
||||||
|
ParsedAccountData,
|
||||||
|
PublicKey,
|
||||||
|
} from "@solana/web3.js";
|
||||||
|
import axios from "axios";
|
||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { DataWrapper } from "../store/helpers";
|
||||||
|
import {
|
||||||
|
COVALENT_GET_TOKENS_URL,
|
||||||
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
SOLANA_HOST,
|
||||||
|
SOL_CUSTODY_ADDRESS,
|
||||||
|
} from "../utils/consts";
|
||||||
|
import useMetadata, { GenericMetadata } from "./useMetadata";
|
||||||
|
|
||||||
|
export type TVL = {
|
||||||
|
logo?: string;
|
||||||
|
symbol?: string;
|
||||||
|
name?: string;
|
||||||
|
amount: string;
|
||||||
|
totalValue?: number;
|
||||||
|
quotePrice?: number;
|
||||||
|
assetAddress: string;
|
||||||
|
originChain: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const calcEthTVL = (covalentReport: any): TVL[] => {
|
||||||
|
const output: TVL[] = [];
|
||||||
|
if (!covalentReport?.data?.items?.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
covalentReport.data.items.forEach((item: any) => {
|
||||||
|
if (item.balance > 0 && item.contract_address) {
|
||||||
|
output.push({
|
||||||
|
logo: item.logo_url || undefined,
|
||||||
|
symbol: item.contract_ticker_symbol || undefined,
|
||||||
|
name: item.contract_name || undefined,
|
||||||
|
amount: formatUnits(item.balance, item.contract_decimals),
|
||||||
|
totalValue: item.quote,
|
||||||
|
quotePrice: item.quote_rate,
|
||||||
|
assetAddress: item.contract_address,
|
||||||
|
originChain: "Ethereum",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
const calcSolanaTVL = (
|
||||||
|
accounts:
|
||||||
|
| { pubkey: PublicKey; account: AccountInfo<ParsedAccountData> }[]
|
||||||
|
| undefined,
|
||||||
|
metaData: DataWrapper<Map<string, GenericMetadata>>
|
||||||
|
) => {
|
||||||
|
const output: TVL[] = [];
|
||||||
|
if (
|
||||||
|
!accounts ||
|
||||||
|
!accounts.length ||
|
||||||
|
metaData.isFetching ||
|
||||||
|
metaData.error ||
|
||||||
|
!metaData.data
|
||||||
|
) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
accounts.forEach((item) => {
|
||||||
|
const genericMetadata = metaData.data?.get(
|
||||||
|
item.account.data.parsed?.info?.mint?.toString()
|
||||||
|
);
|
||||||
|
output.push({
|
||||||
|
logo: genericMetadata?.logo || undefined,
|
||||||
|
symbol: genericMetadata?.symbol || undefined,
|
||||||
|
name: genericMetadata?.tokenName || undefined,
|
||||||
|
amount: item.account.data.parsed?.info?.tokenAmount?.uiAmount || "0", //Should always be defined.
|
||||||
|
totalValue: undefined,
|
||||||
|
quotePrice: undefined,
|
||||||
|
assetAddress: item.account.data.parsed?.info?.mint?.toString(),
|
||||||
|
originChain: "Solana",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return output;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useTVL = (): DataWrapper<TVL[]> => {
|
||||||
|
const [covalentData, setCovalentData] = useState(undefined);
|
||||||
|
const [covalentIsLoading, setCovalentIsLoading] = useState(false);
|
||||||
|
const [covalentError, setCovalentError] = useState("");
|
||||||
|
|
||||||
|
const [solanaCustodyTokens, setSolanaCustodyTokens] = useState<
|
||||||
|
{ pubkey: PublicKey; account: AccountInfo<ParsedAccountData> }[] | undefined
|
||||||
|
>(undefined);
|
||||||
|
const [solanaCustodyTokensLoading, setSolanaCustodyTokensLoading] =
|
||||||
|
useState(false);
|
||||||
|
const [solanaCustodyTokensError, setSolanaCustodyTokensError] = useState("");
|
||||||
|
const mintAddresses = useMemo(() => {
|
||||||
|
const addresses: string[] = [];
|
||||||
|
solanaCustodyTokens?.forEach((item) => {
|
||||||
|
const mintKey = item.account.data.parsed?.info?.mint?.toString();
|
||||||
|
if (mintKey) {
|
||||||
|
addresses.push(mintKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return addresses;
|
||||||
|
}, [solanaCustodyTokens]);
|
||||||
|
|
||||||
|
const solanaMetadata = useMetadata(CHAIN_ID_SOLANA, mintAddresses);
|
||||||
|
|
||||||
|
const solanaTVL = useMemo(
|
||||||
|
() => calcSolanaTVL(solanaCustodyTokens, solanaMetadata),
|
||||||
|
[solanaCustodyTokens, solanaMetadata]
|
||||||
|
);
|
||||||
|
const ethTVL = useMemo(() => calcEthTVL(covalentData), [covalentData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
setCovalentIsLoading(true);
|
||||||
|
axios
|
||||||
|
.get(
|
||||||
|
COVALENT_GET_TOKENS_URL(CHAIN_ID_ETH, ETH_TOKEN_BRIDGE_ADDRESS, false)
|
||||||
|
)
|
||||||
|
.then(
|
||||||
|
(results) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setCovalentData(results.data);
|
||||||
|
setCovalentIsLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setCovalentError("Unable to retrieve Ethereum TVL.");
|
||||||
|
setCovalentIsLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||||
|
connection
|
||||||
|
.getParsedTokenAccountsByOwner(new PublicKey(SOL_CUSTODY_ADDRESS), {
|
||||||
|
programId: TOKEN_PROGRAM_ID,
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
(results) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setSolanaCustodyTokens(results.value);
|
||||||
|
setSolanaCustodyTokensLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
setSolanaCustodyTokensLoading(false);
|
||||||
|
setSolanaCustodyTokensError(
|
||||||
|
"Unable to retrieve Solana locked tokens."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
const tvlArray = [...ethTVL, ...solanaTVL];
|
||||||
|
|
||||||
|
return {
|
||||||
|
isFetching: covalentIsLoading || solanaCustodyTokensLoading,
|
||||||
|
error: covalentError || solanaCustodyTokensError,
|
||||||
|
receivedAt: null,
|
||||||
|
data: tvlArray,
|
||||||
|
};
|
||||||
|
}, [
|
||||||
|
covalentError,
|
||||||
|
covalentIsLoading,
|
||||||
|
ethTVL,
|
||||||
|
solanaTVL,
|
||||||
|
solanaCustodyTokensError,
|
||||||
|
solanaCustodyTokensLoading,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useTVL;
|
|
@ -138,6 +138,9 @@ export const SOL_TOKEN_BRIDGE_ADDRESS =
|
||||||
: CLUSTER === "testnet"
|
: CLUSTER === "testnet"
|
||||||
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
|
? "A4Us8EhCC76XdGAN17L4KpRNEK423nMivVHZzZqFqqBg"
|
||||||
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
|
: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE";
|
||||||
|
|
||||||
|
export const SOL_CUSTODY_ADDRESS =
|
||||||
|
"GugU1tP7doLeTw9hQP51xRJyS8Da1fWxuiy2rVrnMD2m";
|
||||||
export const TERRA_TEST_TOKEN_ADDRESS =
|
export const TERRA_TEST_TOKEN_ADDRESS =
|
||||||
"terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh";
|
"terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh";
|
||||||
export const TERRA_BRIDGE_ADDRESS =
|
export const TERRA_BRIDGE_ADDRESS =
|
||||||
|
|
Loading…
Reference in New Issue