mirror of https://github.com/certusone/oyster.git
added names, symbols and icons to the table
This commit is contained in:
parent
c7a9c0b79a
commit
be907c95f5
|
@ -1,61 +1,38 @@
|
||||||
import {useEffect, useState} from "react";
|
import React, {useEffect, useState} from "react";
|
||||||
import {contexts} from "@oyster/common";
|
import {contexts, TokenIcon, useConnectionConfig} from "@oyster/common";
|
||||||
import * as BufferLayout from 'buffer-layout'
|
import * as BufferLayout from 'buffer-layout'
|
||||||
import {WORMHOLE_PROGRAM_ID} from "../utils/ids";
|
import {WORMHOLE_PROGRAM_ID} from "../utils/ids";
|
||||||
import BN from "bn.js";
|
import BN from "bn.js";
|
||||||
import {ASSET_CHAIN, getAssetAmountInUSD, getAssetName, getAssetTokenSymbol} from "../utils/assets";
|
import {ASSET_CHAIN, getAssetAmountInUSD, getAssetName, getAssetTokenSymbol} from "../utils/assets";
|
||||||
import { useEthereum } from "../contexts";
|
import { useEthereum } from "../contexts";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
import { models } from "@oyster/common";
|
||||||
|
|
||||||
|
const ParsedDataLayout = models.ParsedDataLayout
|
||||||
|
|
||||||
const { useConnection } = contexts.Connection;
|
const { useConnection } = contexts.Connection;
|
||||||
|
|
||||||
interface ParsedData {
|
|
||||||
amount: number,
|
|
||||||
rawAmount: string,
|
|
||||||
parsedAssetAddress: string,
|
|
||||||
parsedAccount: any,
|
|
||||||
assetDecimals: number,
|
|
||||||
name: string,
|
|
||||||
symbol: string,
|
|
||||||
sourceAddress: string,
|
|
||||||
targetAddress: string,
|
|
||||||
amountInUSD: number,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useLockedFundsAccounts = () => {
|
export const useLockedFundsAccounts = () => {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const { tokenMap: ethTokens } = useEthereum();
|
const { tokenMap: ethTokens } = useEthereum();
|
||||||
|
const {tokenMap: solanaTokens} = useConnectionConfig();
|
||||||
|
|
||||||
const [lockedSolanaAccounts, setLockedSolanaAccounts] = useState<ParsedData[]>([]);
|
const [lockedSolanaAccounts, setLockedSolanaAccounts] = useState<models.ParsedDataAccount[]>([]);
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const queryTxs = async () => {
|
const queryTxs = async () => {
|
||||||
|
if (!solanaTokens.size || !ethTokens.size) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const programAccounts = await connection.getProgramAccounts(
|
const programAccounts = await connection.getProgramAccounts(
|
||||||
WORMHOLE_PROGRAM_ID
|
WORMHOLE_PROGRAM_ID
|
||||||
);
|
);
|
||||||
const dataLayout = BufferLayout.struct([
|
|
||||||
BufferLayout.blob(32, 'amount'),
|
const filteredParsedAccounts: models.ParsedDataAccount[] = [];
|
||||||
BufferLayout.u8('toChain'),
|
|
||||||
BufferLayout.blob(32, 'sourceAddress'),
|
|
||||||
BufferLayout.blob(32, 'targetAddress'),
|
|
||||||
BufferLayout.blob(32, 'assetAddress'),
|
|
||||||
BufferLayout.u8('assetChain'),
|
|
||||||
BufferLayout.u8('assetDecimals'),
|
|
||||||
BufferLayout.seq(BufferLayout.u8(), 1), // 4 byte alignment because a u32 is following
|
|
||||||
BufferLayout.u32('nonce'),
|
|
||||||
BufferLayout.blob(1001, 'vaa'),
|
|
||||||
BufferLayout.seq(BufferLayout.u8(), 3), // 4 byte alignment because a u32 is following
|
|
||||||
BufferLayout.u32('vaaTime'),
|
|
||||||
BufferLayout.u32('lockupTime'),
|
|
||||||
BufferLayout.u8('pokeCounter'),
|
|
||||||
BufferLayout.blob(32, 'signatureAccount'),
|
|
||||||
BufferLayout.u8('initialized'),
|
|
||||||
]);
|
|
||||||
const filteredParsedAccounts: ParsedData[] = [];
|
|
||||||
programAccounts.map(acc => {
|
programAccounts.map(acc => {
|
||||||
try {
|
try {
|
||||||
const parsedAccount = dataLayout.decode(acc.account.data)
|
const parsedAccount = ParsedDataLayout.decode(acc.account.data)
|
||||||
const chains = [ASSET_CHAIN.Solana, ASSET_CHAIN.Ethereum]
|
const chains = [ASSET_CHAIN.Solana, ASSET_CHAIN.Ethereum]
|
||||||
if (chains.indexOf(parsedAccount.assetChain) >= 0 &&
|
if (chains.indexOf(parsedAccount.assetChain) >= 0 &&
|
||||||
chains.indexOf(parsedAccount.toChain) >= 0) {
|
chains.indexOf(parsedAccount.toChain) >= 0) {
|
||||||
|
@ -63,20 +40,24 @@ export const useLockedFundsAccounts = () => {
|
||||||
const rawAmount = new BN(parsedAccount.amount, 2, "le")
|
const rawAmount = new BN(parsedAccount.amount, 2, "le")
|
||||||
const amount = rawAmount.div(dec).toNumber();
|
const amount = rawAmount.div(dec).toNumber();
|
||||||
const parsedAssetAddress: string = parsedAccount.assetChain === ASSET_CHAIN.Solana ?
|
const parsedAssetAddress: string = parsedAccount.assetChain === ASSET_CHAIN.Solana ?
|
||||||
new PublicKey(parsedAccount.targetAddress).toString() :
|
new PublicKey(parsedAccount.assetAddress).toString() :
|
||||||
new Buffer(parsedAccount.assetAddress.slice(12)).toString("hex")
|
new Buffer(parsedAccount.assetAddress.slice(12)).toString("hex")
|
||||||
const parsedData: ParsedData = {
|
|
||||||
|
const parsedData: models.ParsedDataAccount = {
|
||||||
amount: amount,
|
amount: amount,
|
||||||
rawAmount: rawAmount.toString(),
|
rawAmount: rawAmount.toString(),
|
||||||
parsedAssetAddress: parsedAssetAddress,
|
parsedAssetAddress: parsedAssetAddress,
|
||||||
parsedAccount: parsedAccount,
|
parsedAccount: parsedAccount,
|
||||||
assetDecimals: parsedAccount.assetDecimals,
|
assetDecimals: parsedAccount.assetDecimals,
|
||||||
|
assetIcon: parsedAccount.assetChain === ASSET_CHAIN.Solana ?
|
||||||
|
<TokenIcon mintAddress={parsedAssetAddress} /> :
|
||||||
|
<TokenIcon mintAddress={`0x${parsedAssetAddress}`} tokenMap={ethTokens} />,
|
||||||
sourceAddress: new PublicKey(parsedAccount.sourceAddress).toString(),
|
sourceAddress: new PublicKey(parsedAccount.sourceAddress).toString(),
|
||||||
targetAddress: parsedAccount.toChain === ASSET_CHAIN.Solana ?
|
targetAddress: parsedAccount.toChain === ASSET_CHAIN.Solana ?
|
||||||
new PublicKey(parsedAccount.targetAddress).toString()
|
new PublicKey(parsedAccount.targetAddress).toString()
|
||||||
: new Buffer(parsedAccount.targetAddress.slice(12)).toString("hex"),
|
: new Buffer(parsedAccount.targetAddress.slice(12)).toString("hex"),
|
||||||
name: getAssetName(parsedAssetAddress, parsedAccount.assetChain),
|
name: getAssetName(parsedAssetAddress, parsedAccount.assetChain, solanaTokens, ethTokens),
|
||||||
symbol: getAssetTokenSymbol(parsedAssetAddress, parsedAccount.assetChain),
|
symbol: getAssetTokenSymbol(parsedAssetAddress, parsedAccount.assetChain, solanaTokens, ethTokens),
|
||||||
amountInUSD: getAssetAmountInUSD(amount, parsedAssetAddress, parsedAccount.assetChain),
|
amountInUSD: getAssetAmountInUSD(amount, parsedAssetAddress, parsedAccount.assetChain),
|
||||||
};
|
};
|
||||||
filteredParsedAccounts.push(parsedData)
|
filteredParsedAccounts.push(parsedData)
|
||||||
|
@ -91,7 +72,7 @@ export const useLockedFundsAccounts = () => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setLockedSolanaAccounts(all[0])
|
setLockedSolanaAccounts(all[0])
|
||||||
});
|
});
|
||||||
}, []);
|
}, [solanaTokens, ethTokens]);
|
||||||
return {
|
return {
|
||||||
loading,
|
loading,
|
||||||
lockedSolanaAccounts,
|
lockedSolanaAccounts,
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
|
import {
|
||||||
|
getTokenName,
|
||||||
|
getVerboseTokenName,
|
||||||
|
KnownTokenMap,
|
||||||
|
TokenIcon,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
export const getAssetName = (
|
export const getAssetName = (
|
||||||
parsedAssetAddress: string,
|
parsedAssetAddress: string,
|
||||||
assetChain: number,
|
assetChain: number,
|
||||||
|
solanaTokens: KnownTokenMap,
|
||||||
|
ethTokens: KnownTokenMap,
|
||||||
) => {
|
) => {
|
||||||
return parsedAssetAddress.slice(0, 5);
|
if (assetChain === ASSET_CHAIN.Solana)
|
||||||
|
return getVerboseTokenName(solanaTokens, parsedAssetAddress);
|
||||||
|
else return getVerboseTokenName(ethTokens, `0x${parsedAssetAddress}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssetTokenSymbol = (
|
export const getAssetTokenSymbol = (
|
||||||
parsedAssetAddress: string,
|
parsedAssetAddress: string,
|
||||||
assetChain: number,
|
assetChain: number,
|
||||||
|
solanaTokens: KnownTokenMap,
|
||||||
|
ethTokens: KnownTokenMap,
|
||||||
) => {
|
) => {
|
||||||
return parsedAssetAddress.slice(0, 5);
|
if (assetChain === ASSET_CHAIN.Solana)
|
||||||
|
return getTokenName(solanaTokens, parsedAssetAddress);
|
||||||
|
else return getTokenName(ethTokens, `0x${parsedAssetAddress}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssetAmountInUSD = (
|
export const getAssetAmountInUSD = (
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { MintInfo } from '@solana/spl-token';
|
||||||
import { Table, Tag, Space, Card, Col, Row, Statistic, Button } from 'antd';
|
import { Table, Tag, Space, Card, Col, Row, Statistic, Button } from 'antd';
|
||||||
import React, {useEffect, useMemo, useState} from 'react';
|
import React, {useEffect, useMemo, useState} from 'react';
|
||||||
import { GUTTER, LABELS } from '../../constants';
|
import { GUTTER, LABELS } from '../../constants';
|
||||||
import {contexts, ExplorerLink, ParsedAccount, utils} from '@oyster/common';
|
import {contexts, ExplorerLink, ParsedAccount, TokenIcon, utils} from '@oyster/common';
|
||||||
import { useMarkets } from '../../contexts/market';
|
import { useMarkets } from '../../contexts/market';
|
||||||
|
|
||||||
import { LendingReserveItem } from './item';
|
import { LendingReserveItem } from './item';
|
||||||
|
@ -86,13 +86,13 @@ export const HomeView = () => {
|
||||||
return lockedSolanaAccounts.map((acc, index) => {
|
return lockedSolanaAccounts.map((acc, index) => {
|
||||||
return {
|
return {
|
||||||
key: index.toString(),
|
key: index.toString(),
|
||||||
symbol: acc.symbol,
|
symbol: <div>{acc.assetIcon} {acc.symbol}</div>,
|
||||||
name: acc.name,
|
name: acc.name,
|
||||||
amount: acc.amountInUSD,
|
amount: acc.amountInUSD,
|
||||||
assetAddress: acc.parsedAccount.assetChain === ASSET_CHAIN.Solana ?
|
assetAddress: acc.parsedAccount.assetChain === ASSET_CHAIN.Solana ?
|
||||||
<ExplorerLink address={acc.parsedAssetAddress} type={"address"} /> :
|
<ExplorerLink address={acc.parsedAssetAddress} type={"address"} /> :
|
||||||
<EtherscanLink address={acc.parsedAssetAddress} type={"address"} />,
|
<EtherscanLink address={acc.parsedAssetAddress} type={"address"} />,
|
||||||
sourceAddress: <ExplorerLink address={acc.sourceAddress} type={"address"} />,
|
sourceAddress: <ExplorerLink address={acc.sourceAddress} type={"address"} />,
|
||||||
targetAddress: acc.parsedAccount.toChain === ASSET_CHAIN.Solana ?
|
targetAddress: acc.parsedAccount.toChain === ASSET_CHAIN.Solana ?
|
||||||
<ExplorerLink address={acc.targetAddress} type={"address"} /> :
|
<ExplorerLink address={acc.targetAddress} type={"address"} /> :
|
||||||
<EtherscanLink address={acc.targetAddress} type={"address"} />,
|
<EtherscanLink address={acc.targetAddress} type={"address"} />,
|
||||||
|
@ -133,8 +133,6 @@ export const HomeView = () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
debugger;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flexColumn">
|
<div className="flexColumn">
|
||||||
<Row
|
<Row
|
||||||
|
|
|
@ -19,6 +19,8 @@ export const Identicon = (props: {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (address && ref.current) {
|
if (address && ref.current) {
|
||||||
|
try {
|
||||||
|
|
||||||
ref.current.innerHTML = '';
|
ref.current.innerHTML = '';
|
||||||
ref.current.className = className || '';
|
ref.current.className = className || '';
|
||||||
ref.current.appendChild(
|
ref.current.appendChild(
|
||||||
|
@ -27,6 +29,10 @@ export const Identicon = (props: {
|
||||||
parseInt(bs58.decode(address).toString('hex').slice(5, 15), 16),
|
parseInt(bs58.decode(address).toString('hex').slice(5, 15), 16),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
}catch (err) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [address, style, className]);
|
}, [address, style, className]);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from '@solana/web3.js';
|
||||||
import { getTokenIcon } from '../../utils';
|
import {getTokenIcon, KnownTokenMap} from '../../utils';
|
||||||
import { useConnectionConfig } from '../../contexts/connection';
|
import { useConnectionConfig } from '../../contexts/connection';
|
||||||
import { Identicon } from '../Identicon';
|
import { Identicon } from '../Identicon';
|
||||||
|
|
||||||
|
@ -9,9 +9,15 @@ export const TokenIcon = (props: {
|
||||||
style?: React.CSSProperties;
|
style?: React.CSSProperties;
|
||||||
size?: number;
|
size?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
tokenMap?: KnownTokenMap,
|
||||||
}) => {
|
}) => {
|
||||||
const { tokenMap } = useConnectionConfig();
|
let icon: string | undefined = '';
|
||||||
const icon = getTokenIcon(tokenMap, props.mintAddress);
|
if (props.tokenMap) {
|
||||||
|
icon = getTokenIcon(props.tokenMap, props.mintAddress);
|
||||||
|
} else {
|
||||||
|
const { tokenMap } = useConnectionConfig();
|
||||||
|
icon = getTokenIcon(tokenMap, props.mintAddress);
|
||||||
|
}
|
||||||
|
|
||||||
const size = props.size || 20;
|
const size = props.size || 20;
|
||||||
|
|
||||||
|
@ -35,7 +41,6 @@ export const TokenIcon = (props: {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Identicon
|
<Identicon
|
||||||
address={props.mintAddress}
|
address={props.mintAddress}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
|
|
||||||
import { AccountInfo as TokenAccountInfo, Token } from '@solana/spl-token';
|
import { AccountInfo as TokenAccountInfo, Token } from '@solana/spl-token';
|
||||||
import { TOKEN_PROGRAM_ID } from '../utils/ids';
|
import { TOKEN_PROGRAM_ID } from '../utils/ids';
|
||||||
|
import BufferLayout from 'buffer-layout';
|
||||||
|
|
||||||
export interface TokenAccount {
|
export interface TokenAccount {
|
||||||
pubkey: PublicKey;
|
pubkey: PublicKey;
|
||||||
|
@ -14,6 +15,39 @@ export interface TokenAccount {
|
||||||
info: TokenAccountInfo;
|
info: TokenAccountInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ParsedDataAccount {
|
||||||
|
amount: number;
|
||||||
|
rawAmount: string;
|
||||||
|
parsedAssetAddress: string;
|
||||||
|
parsedAccount: any;
|
||||||
|
assetDecimals: number;
|
||||||
|
assetIcon: any;
|
||||||
|
name: string;
|
||||||
|
symbol: string;
|
||||||
|
sourceAddress: string;
|
||||||
|
targetAddress: string;
|
||||||
|
amountInUSD: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ParsedDataLayout = BufferLayout.struct([
|
||||||
|
BufferLayout.blob(32, 'amount'),
|
||||||
|
BufferLayout.u8('toChain'),
|
||||||
|
BufferLayout.blob(32, 'sourceAddress'),
|
||||||
|
BufferLayout.blob(32, 'targetAddress'),
|
||||||
|
BufferLayout.blob(32, 'assetAddress'),
|
||||||
|
BufferLayout.u8('assetChain'),
|
||||||
|
BufferLayout.u8('assetDecimals'),
|
||||||
|
BufferLayout.seq(BufferLayout.u8(), 1), // 4 byte alignment because a u32 is following
|
||||||
|
BufferLayout.u32('nonce'),
|
||||||
|
BufferLayout.blob(1001, 'vaa'),
|
||||||
|
BufferLayout.seq(BufferLayout.u8(), 3), // 4 byte alignment because a u32 is following
|
||||||
|
BufferLayout.u32('vaaTime'),
|
||||||
|
BufferLayout.u32('lockupTime'),
|
||||||
|
BufferLayout.u8('pokeCounter'),
|
||||||
|
BufferLayout.blob(32, 'signatureAccount'),
|
||||||
|
BufferLayout.u8('initialized'),
|
||||||
|
]);
|
||||||
|
|
||||||
export function approve(
|
export function approve(
|
||||||
instructions: TransactionInstruction[],
|
instructions: TransactionInstruction[],
|
||||||
cleanupInstructions: TransactionInstruction[],
|
cleanupInstructions: TransactionInstruction[],
|
||||||
|
|
|
@ -67,6 +67,24 @@ export function getTokenName(
|
||||||
|
|
||||||
return shorten ? `${mintAddress.substring(0, 5)}...` : mintAddress;
|
return shorten ? `${mintAddress.substring(0, 5)}...` : mintAddress;
|
||||||
}
|
}
|
||||||
|
export function getVerboseTokenName(
|
||||||
|
map: KnownTokenMap,
|
||||||
|
mint?: string | PublicKey,
|
||||||
|
shorten = true,
|
||||||
|
): string {
|
||||||
|
const mintAddress = typeof mint === 'string' ? mint : mint?.toBase58();
|
||||||
|
|
||||||
|
if (!mintAddress) {
|
||||||
|
return 'N/A';
|
||||||
|
}
|
||||||
|
|
||||||
|
const knownName = map.get(mintAddress)?.name;
|
||||||
|
if (knownName) {
|
||||||
|
return knownName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shorten ? `${mintAddress.substring(0, 5)}...` : mintAddress;
|
||||||
|
}
|
||||||
|
|
||||||
export function getTokenByName(tokenMap: KnownTokenMap, name: string) {
|
export function getTokenByName(tokenMap: KnownTokenMap, name: string) {
|
||||||
let token: TokenInfo | null = null;
|
let token: TokenInfo | null = null;
|
||||||
|
|
Loading…
Reference in New Issue