added names, symbols and icons to the table

This commit is contained in:
juan 2021-03-04 16:35:07 -05:00
parent c7a9c0b79a
commit be907c95f5
7 changed files with 110 additions and 52 deletions

View File

@ -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,

View File

@ -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 = (

View File

@ -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,7 +86,7 @@ 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 ?
@ -133,8 +133,6 @@ export const HomeView = () => {
}, },
]; ];
debugger;
return ( return (
<div className="flexColumn"> <div className="flexColumn">
<Row <Row

View File

@ -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]);

View File

@ -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,
}) => { }) => {
let icon: string | undefined = '';
if (props.tokenMap) {
icon = getTokenIcon(props.tokenMap, props.mintAddress);
} else {
const { tokenMap } = useConnectionConfig(); const { tokenMap } = useConnectionConfig();
const icon = getTokenIcon(tokenMap, props.mintAddress); 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}

View File

@ -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[],

View File

@ -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;