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 {
CHAINS,
CHAINS_BY_ID,
ETH_NFT_BRIDGE_ADDRESS,
SOLANA_HOST,
SOL_NFT_BRIDGE_ADDRESS,
@ -294,6 +295,9 @@ export default function NFTOriginVerifier() {
>
Origin Info
</Typography>
<Typography variant="body2" gutterBottom>
Chain: {CHAINS_BY_ID[originInfo.chainId].name}
</Typography>
<Typography variant="body2" gutterBottom>
Address: {readableAddress}
</Typography>

View File

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

View File

@ -13,7 +13,7 @@ import {
MIGRATION_ASSET_MAP,
WORMHOLE_V1_MINT_AUTHORITY,
} from "../../utils/consts";
import { shortenAddress } from "../../utils/solana";
import { ExtractedMintInfo, shortenAddress } from "../../utils/solana";
import NFTViewer from "./NFTViewer";
import RefreshButtonWrapper from "./RefreshButtonWrapper";
@ -37,7 +37,9 @@ type SolanaSourceTokenSelectorProps = {
onChange: (newValue: ParsedTokenAccount | null) => void;
accounts: ParsedTokenAccount[];
disabled: boolean;
mintAccounts: DataWrapper<Map<string, string | null>> | undefined;
mintAccounts:
| DataWrapper<Map<string, ExtractedMintInfo | null> | undefined>
| undefined;
resetAccounts: (() => void) | undefined;
nft?: boolean;
};
@ -135,13 +137,13 @@ export default function SolanaSourceTokenSelector(
if (!props.mintAccounts?.data) {
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.
}
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.
}
@ -233,16 +235,17 @@ export default function SolanaSourceTokenSelector(
//difficult to do before this point.
const filteredOptions = useMemo(() => {
return props.accounts.filter((x) => {
//TODO, do a better check which likely involves supply or checking masterEdition.
const zeroBalance = x.amount === "0";
if (zeroBalance) {
return false;
}
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;
});
}, [metaplex.data, nft, props.accounts]);
}, [mintAccounts?.data, metaplex.data, nft, props.accounts]);
const isOptionDisabled = useMemo(() => {
return (value: ParsedTokenAccount) => {

View File

@ -55,7 +55,8 @@ import {
WETH_DECIMALS,
} from "../utils/consts";
import {
extractMintAuthorityInfo,
ExtractedMintInfo,
extractMintInfo,
getMultipleAccountsRPC,
} from "../utils/solana";
@ -336,7 +337,9 @@ function useGetAvailableTokens(nft: boolean = false) {
string | undefined
>(undefined);
const [solanaMintAccounts, setSolanaMintAccounts] = useState<any>(undefined);
const [solanaMintAccounts, setSolanaMintAccounts] = useState<
Map<string, ExtractedMintInfo | null> | undefined
>(undefined);
const [solanaMintAccountsLoading, setSolanaMintAccountsLoading] =
useState(false);
const [solanaMintAccountsError, setSolanaMintAccountsError] = useState<
@ -425,6 +428,10 @@ function useGetAvailableTokens(nft: boolean = false) {
// mintAddresses.push("4QixXecTZ4zdZGa39KH8gVND5NZ2xcaB12wiBhE4S7rn");
//SOLT devnet token
// mintAddresses.push("2WDq7wSs9zYrpx2kbHDA4RUTRch2CCTP6ZWaH4GNfnQQ");
// bad monkey "NFT"
// mintAddresses.push("5FJeEJR8576YxXFdGRAu4NBBFcyfmtjsZrXHSsnzNPdS");
// degenerate monkey NFT
// mintAddresses.push("EzYsbigNNGbNuANRJ3mnnyJYU2Bk7mBYVsxuonUwAX7r");
const connection = new Connection(SOLANA_HOST, "confirmed");
getMultipleAccountsRPC(
@ -433,12 +440,12 @@ function useGetAvailableTokens(nft: boolean = false) {
).then(
(results) => {
if (!cancelled) {
const output = new Map<String, string | null>();
const output = new Map<string, ExtractedMintInfo | null>();
results.forEach((result, index) =>
output.set(
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 { WalletContextState } from "@solana/wallet-adapter-react";
import {
@ -21,17 +22,26 @@ export async function signSendAndConfirm(
return txid;
}
export function extractMintAuthorityInfo(
export interface ExtractedMintInfo {
mintAuthority?: string;
supply?: string;
}
export function extractMintInfo(
account: AccountInfo<Buffer>
): string | null {
): ExtractedMintInfo {
const data = Buffer.from(account.data);
const mintInfo = MintLayout.decode(data);
const uintArray = mintInfo?.mintAuthority;
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(