bridge_ui: nft supply check
Change-Id: Ib74c0add8a093ccfef571fc345951f6a818cb75c
This commit is contained in:
parent
8dd29f2b88
commit
302368d704
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue