bridge_ui: nft supply check

Change-Id: Ib74c0add8a093ccfef571fc345951f6a818cb75c
This commit is contained in:
Evan Gray 2021-09-23 12:03:40 -04:00
parent 8dd29f2b88
commit 302368d704
5 changed files with 41 additions and 17 deletions

View File

@ -30,6 +30,7 @@ import { NFTParsedTokenAccount } from "../store/nftSlice";
import { hexToNativeString, uint8ArrayToHex } from "../utils/array"; import { hexToNativeString, uint8ArrayToHex } from "../utils/array";
import { import {
CHAINS, CHAINS,
CHAINS_BY_ID,
ETH_NFT_BRIDGE_ADDRESS, ETH_NFT_BRIDGE_ADDRESS,
SOLANA_HOST, SOLANA_HOST,
SOL_NFT_BRIDGE_ADDRESS, SOL_NFT_BRIDGE_ADDRESS,
@ -294,6 +295,9 @@ export default function NFTOriginVerifier() {
> >
Origin Info Origin Info
</Typography> </Typography>
<Typography variant="body2" gutterBottom>
Chain: {CHAINS_BY_ID[originInfo.chainId].name}
</Typography>
<Typography variant="body2" gutterBottom> <Typography variant="body2" gutterBottom>
Address: {readableAddress} Address: {readableAddress}
</Typography> </Typography>

View File

@ -236,7 +236,7 @@ export default function NFTViewer({
})} })}
> >
{hasVideo ? ( {hasVideo ? (
<video autoPlay controls style={{ maxWidth: "100%" }}> <video autoPlay controls loop style={{ maxWidth: "100%" }}>
<source src={safeIPFS(metadata.animation_url || "")} /> <source src={safeIPFS(metadata.animation_url || "")} />
{image} {image}
</video> </video>

View File

@ -13,7 +13,7 @@ import {
MIGRATION_ASSET_MAP, MIGRATION_ASSET_MAP,
WORMHOLE_V1_MINT_AUTHORITY, WORMHOLE_V1_MINT_AUTHORITY,
} from "../../utils/consts"; } from "../../utils/consts";
import { shortenAddress } from "../../utils/solana"; import { ExtractedMintInfo, shortenAddress } from "../../utils/solana";
import NFTViewer from "./NFTViewer"; import NFTViewer from "./NFTViewer";
import RefreshButtonWrapper from "./RefreshButtonWrapper"; import RefreshButtonWrapper from "./RefreshButtonWrapper";
@ -37,7 +37,9 @@ type SolanaSourceTokenSelectorProps = {
onChange: (newValue: ParsedTokenAccount | null) => void; onChange: (newValue: ParsedTokenAccount | null) => void;
accounts: ParsedTokenAccount[]; accounts: ParsedTokenAccount[];
disabled: boolean; disabled: boolean;
mintAccounts: DataWrapper<Map<string, string | null>> | undefined; mintAccounts:
| DataWrapper<Map<string, ExtractedMintInfo | null> | undefined>
| undefined;
resetAccounts: (() => void) | undefined; resetAccounts: (() => void) | undefined;
nft?: boolean; nft?: boolean;
}; };
@ -135,13 +137,13 @@ export default function SolanaSourceTokenSelector(
if (!props.mintAccounts?.data) { if (!props.mintAccounts?.data) {
return true; //These should never be null by this point return true; //These should never be null by this point
} }
const mintInfo = props.mintAccounts.data.get(address); const mintAuthority = props.mintAccounts.data.get(address)?.mintAuthority;
if (!mintInfo) { if (!mintAuthority) {
return true; //We should never fail to pull the mint of an account. return true; //We should never fail to pull the mint of an account.
} }
if (mintInfo === WORMHOLE_V1_MINT_AUTHORITY) { if (mintAuthority === WORMHOLE_V1_MINT_AUTHORITY) {
return true; //This means the mint was created by the wormhole v1 contract, and we want to disallow its transfer. return true; //This means the mint was created by the wormhole v1 contract, and we want to disallow its transfer.
} }
@ -233,16 +235,17 @@ export default function SolanaSourceTokenSelector(
//difficult to do before this point. //difficult to do before this point.
const filteredOptions = useMemo(() => { const filteredOptions = useMemo(() => {
return props.accounts.filter((x) => { return props.accounts.filter((x) => {
//TODO, do a better check which likely involves supply or checking masterEdition.
const zeroBalance = x.amount === "0"; const zeroBalance = x.amount === "0";
if (zeroBalance) { if (zeroBalance) {
return false; return false;
} }
const isNFT = const isNFT =
x.decimals === 0 && metaplex.data?.get(x.mintKey)?.data?.uri; x.decimals === 0 &&
metaplex.data?.get(x.mintKey)?.data?.uri &&
mintAccounts?.data?.get(x.mintKey)?.supply === "1";
return nft ? isNFT : !isNFT; return nft ? isNFT : !isNFT;
}); });
}, [metaplex.data, nft, props.accounts]); }, [mintAccounts?.data, metaplex.data, nft, props.accounts]);
const isOptionDisabled = useMemo(() => { const isOptionDisabled = useMemo(() => {
return (value: ParsedTokenAccount) => { return (value: ParsedTokenAccount) => {

View File

@ -55,7 +55,8 @@ import {
WETH_DECIMALS, WETH_DECIMALS,
} from "../utils/consts"; } from "../utils/consts";
import { import {
extractMintAuthorityInfo, ExtractedMintInfo,
extractMintInfo,
getMultipleAccountsRPC, getMultipleAccountsRPC,
} from "../utils/solana"; } from "../utils/solana";
@ -336,7 +337,9 @@ function useGetAvailableTokens(nft: boolean = false) {
string | undefined string | undefined
>(undefined); >(undefined);
const [solanaMintAccounts, setSolanaMintAccounts] = useState<any>(undefined); const [solanaMintAccounts, setSolanaMintAccounts] = useState<
Map<string, ExtractedMintInfo | null> | undefined
>(undefined);
const [solanaMintAccountsLoading, setSolanaMintAccountsLoading] = const [solanaMintAccountsLoading, setSolanaMintAccountsLoading] =
useState(false); useState(false);
const [solanaMintAccountsError, setSolanaMintAccountsError] = useState< const [solanaMintAccountsError, setSolanaMintAccountsError] = useState<
@ -425,6 +428,10 @@ function useGetAvailableTokens(nft: boolean = false) {
// mintAddresses.push("4QixXecTZ4zdZGa39KH8gVND5NZ2xcaB12wiBhE4S7rn"); // mintAddresses.push("4QixXecTZ4zdZGa39KH8gVND5NZ2xcaB12wiBhE4S7rn");
//SOLT devnet token //SOLT devnet token
// mintAddresses.push("2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ"); // mintAddresses.push("2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ");
// bad monkey "NFT"
// mintAddresses.push("5FJeEJR8576YxXFdGRAu4NBBFcyfmtjsZrXHSsnzNPdS");
// degenerate monkey NFT
// mintAddresses.push("EzYsbigNNGbNuANRJ3mnnyJYU2Bk7mBYVsxuonUwAX7r");
const connection = new Connection(SOLANA_HOST, "confirmed"); const connection = new Connection(SOLANA_HOST, "confirmed");
getMultipleAccountsRPC( getMultipleAccountsRPC(
@ -433,12 +440,12 @@ function useGetAvailableTokens(nft: boolean = false) {
).then( ).then(
(results) => { (results) => {
if (!cancelled) { if (!cancelled) {
const output = new Map<String, string | null>(); const output = new Map<string, ExtractedMintInfo | null>();
results.forEach((result, index) => results.forEach((result, index) =>
output.set( output.set(
mintAddresses[index], mintAddresses[index],
(result && extractMintAuthorityInfo(result)) || null (result && extractMintInfo(result)) || null
) )
); );

View File

@ -1,3 +1,4 @@
import { BigNumber } from "@ethersproject/bignumber";
import { MintLayout } from "@solana/spl-token"; import { MintLayout } from "@solana/spl-token";
import { WalletContextState } from "@solana/wallet-adapter-react"; import { WalletContextState } from "@solana/wallet-adapter-react";
import { import {
@ -21,17 +22,26 @@ export async function signSendAndConfirm(
return txid; return txid;
} }
export function extractMintAuthorityInfo( export interface ExtractedMintInfo {
mintAuthority?: string;
supply?: string;
}
export function extractMintInfo(
account: AccountInfo<Buffer> account: AccountInfo<Buffer>
): string | null { ): ExtractedMintInfo {
const data = Buffer.from(account.data); const data = Buffer.from(account.data);
const mintInfo = MintLayout.decode(data); const mintInfo = MintLayout.decode(data);
const uintArray = mintInfo?.mintAuthority; const uintArray = mintInfo?.mintAuthority;
const pubkey = new PublicKey(uintArray); const pubkey = new PublicKey(uintArray);
const output = pubkey?.toString(); const supply = BigNumber.from(mintInfo?.supply.reverse()).toString();
const output = {
mintAuthority: pubkey?.toString(),
supply: supply.toString(),
};
return output || null; return output;
} }
export async function getMultipleAccountsRPC( export async function getMultipleAccountsRPC(