diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 824f0da6..78f57b7b 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/src/components/Modals/ImportNFTModal/ImportNFTModal.jsx b/src/components/Modals/ImportNFTModal/ImportNFTModal.jsx index 174758a1..d4f36ae8 100755 --- a/src/components/Modals/ImportNFTModal/ImportNFTModal.jsx +++ b/src/components/Modals/ImportNFTModal/ImportNFTModal.jsx @@ -3,19 +3,25 @@ import { Modal } from "react-bootstrap"; import { useDispatch, useSelector } from "react-redux"; import { setImportModal, - addImportedNFTtoNFTlist, + setPreloadNFTs, + setNFTList, } from "../../../store/reducers/generalSlice"; import { validateFunctions } from "../../../services/addressValidators"; - -import axios from "axios"; import "./importNFTModal.css"; -import EVMBody from "./EVMBody"; +import EVMBody from "./importBodies/EVMBody"; +import { withServices } from "../../App/hocs/withServices"; +import { + checkNFTExist, + importNFTURI, + validForm, +} from "../../../utils/importNFTUtility"; -export default function ImportNFTModal() { +function ImportNFTModal({ serviceContainer }) { const dispatch = useDispatch(); const from = useSelector((state) => state.general.from); const account = useSelector((state) => state.general.account); + const NFTList = useSelector((state) => state.general.NFTList); const [validContract, setValidContract] = useState(true); const [contract, setContract] = useState(); @@ -23,8 +29,6 @@ export default function ImportNFTModal() { const [tokenId, setTokenId] = useState(); const [importBlocked, setImportBlocked] = useState(false); const [error, setError] = useState(""); - const validForm = contract?.length === 42 && tokenId; - const chainNonce = from.nonce; const handleClose = () => { dispatch(setImportModal(false)); @@ -36,51 +40,52 @@ export default function ImportNFTModal() { // setValidContract(true); // } else setValidContract(false); - if(value.length > 0){ - setValidContract(validateFunctions.EVM(value)) - } - else{ - setValidContract(true) + if (value.length > 0 && from.type === "EVM") { + setValidContract(validateFunctions.EVM(value)); + } else if (from.type === "Elrond") { + console.log("Elrond"); + setValidContract(validateFunctions.Elrond(value)); + } else { + setValidContract(true); } }; - //" "; - //"http://192.168.129.241:3000/nfts/nftCheck"; + /** + * + * IMPORTING TOKEN + */ const handleImport = async () => { - const baseURL = "https://indexnft.herokuapp.com/nfts/nftCheck"; - const _headers = { - Accept: "*", - "Content-Type": "application/json", - // Authorization: `Bearer ${process.env.REACT_APP_BEARER}`, - }; try { setImportBlocked(true); - setTimeout(() => { - setImportBlocked(false); - }, 10000); - const imported = await axios({ - method: "post", - url: baseURL, - headers: _headers, - data: JSON.stringify({ - chainNonce, - tokenId, - contract, - address: account, - }), - }); - setImportBlocked(false); - if (typeof imported.data === "object") { - dispatch(addImportedNFTtoNFTlist(imported.data)); - } else setError(imported.data); + + const fromChain = await serviceContainer.bridge.getChain(from.nonce); + const signer = fromChain.signer; + + if (checkNFTExist(NFTList, contract, tokenId, from)) + throw new Error("NFT already imported!"); + + const formattedData = await importNFTURI( + contract, + tokenId, + account, + signer, + from + ); + + dispatch(setPreloadNFTs(NFTList ? NFTList.length + 1 : 1)); + dispatch(setNFTList(NFTList ? [...NFTList, formattedData] : [formattedData])); dispatch(setImportModal(false)); - } catch (error) { - setError(error.message); setImportBlocked(false); - console.error(error); + setError(''); + + } catch (err) { + console.log(err); + setImportBlocked(false); + setError(err.message || "You don't own this NFT!"); } }; + return (
@@ -98,7 +103,7 @@ export default function ImportNFTModal() { setTokenId={setTokenId} importBlocked={importBlocked} error={error} - validForm={validForm} + validForm={validForm(from, contract, tokenId)} // OFF={OFF} handleClose={handleClose} handleContractChange={handleContractChange} @@ -107,3 +112,7 @@ export default function ImportNFTModal() {
); } + + + +export default withServices(ImportNFTModal) \ No newline at end of file diff --git a/src/components/Modals/ImportNFTModal/importBodies/EVMBody.jsx b/src/components/Modals/ImportNFTModal/importBodies/EVMBody.jsx new file mode 100755 index 00000000..be6292af --- /dev/null +++ b/src/components/Modals/ImportNFTModal/importBodies/EVMBody.jsx @@ -0,0 +1,100 @@ +import React from "react"; +import { Modal } from "react-bootstrap"; +import PropTypes from "prop-types"; +import { useSelector } from "react-redux"; +import { importInputs } from "../../../../utils/importNFTUtility"; + +export default function EVMBody({ + error, + tokenId, + handleImport, + handleClose, + validForm, + importBlocked, + validContract, + setTokenId, + setContractOnBlur, + contract, + contractOnBlur, + handleContractChange, +}) { + const from = useSelector((state) => state.general.from); + const OFF = { opacity: 0.6, pointerEvents: "none" }; + return ( + + {error &&
{error}
} +
+
{e.preventDefault()})}> +
+ + setContractOnBlur(true)} + onChange={(e) => { + handleContractChange(e.target.value); + setContractOnBlur(true); + }} + type="text" + id="contractAdd" + name="contractAddress" + placeholder={importInputs(from).contract.placeholder} + value={contract} + className={ + validContract + ? "contract__input--valid" + : "contract__input--invalid" + } + /> + {contractOnBlur && !validContract && ( + + Error Contract Address + + )} +
+ {from.type !== "TON" && from.type !== "Solana" ? ( +
+ + setTokenId(e.target.value)} + type="text" + id="tokedId" + name="tokenId" + placeholder={importInputs(from).tokenId.placeholder} + value={tokenId} + /> +
+ ) : ( + "" + )} +
+
+ Import +
+
+ Cancel +
+
+
+
+
+ ); +} +EVMBody.propTypes = { + error: PropTypes.string, + tokenId: PropTypes.string, + handleImport: PropTypes.any, + handleClose: PropTypes.any, + validForm: PropTypes.bool, + importBlocked: PropTypes.bool, + validContract: PropTypes.bool, + setTokenId: PropTypes.any, + setContractOnBlur: PropTypes.any, + contract: PropTypes.string, + contractOnBlur: PropTypes.string, + handleContractChange: PropTypes.any, +}; diff --git a/src/components/Modals/ImportNFTModal/EVMBody.jsx b/src/components/Modals/ImportNFTModal/importBodies/HederaBody.jsx old mode 100755 new mode 100644 similarity index 89% rename from src/components/Modals/ImportNFTModal/EVMBody.jsx rename to src/components/Modals/ImportNFTModal/importBodies/HederaBody.jsx index fc377bbc..4d34f1a4 --- a/src/components/Modals/ImportNFTModal/EVMBody.jsx +++ b/src/components/Modals/ImportNFTModal/importBodies/HederaBody.jsx @@ -2,7 +2,7 @@ import React from "react"; import { Modal } from "react-bootstrap"; import PropTypes from "prop-types"; -export default function EVMBody({ +export default function HederaBody({ error, tokenId, handleImport, @@ -23,7 +23,7 @@ export default function EVMBody({
{e.preventDefault()})}>
- + setContractOnBlur(true)} onChange={(e) => { @@ -33,7 +33,7 @@ export default function EVMBody({ type="text" id="contractAdd" name="contractAddress" - placeholder="0x..." + placeholder="0.0.432..." value={contract} className={ validContract @@ -48,13 +48,13 @@ export default function EVMBody({ )}
- + setTokenId(e.target.value)} type="text" id="tokedId" name="tokenId" - placeholder="Enter Token ID" + placeholder="1" value={tokenId} />
@@ -75,7 +75,7 @@ export default function EVMBody({ ); } -EVMBody.propTypes = { +HederaBody.propTypes = { error: PropTypes.string, tokenId: PropTypes.string, handleImport: PropTypes.any, diff --git a/src/components/Modals/ImportNFTModal/importBodies/MultiversexBody.jsx b/src/components/Modals/ImportNFTModal/importBodies/MultiversexBody.jsx new file mode 100644 index 00000000..9b583d2d --- /dev/null +++ b/src/components/Modals/ImportNFTModal/importBodies/MultiversexBody.jsx @@ -0,0 +1,91 @@ +import React from "react"; +import { Modal } from "react-bootstrap"; +import PropTypes from "prop-types"; + +export default function MultiversexBody({ + error, + tokenId, + handleImport, + handleClose, + validForm, + importBlocked, + validContract, + setTokenId, + setContractOnBlur, + contract, + contractOnBlur, + handleContractChange, +}) { + const OFF = { opacity: 0.6, pointerEvents: "none" }; + return ( + + {error &&
{error}
} +
+ {e.preventDefault()})}> +
+ + setContractOnBlur(true)} + onChange={(e) => { + handleContractChange(e.target.value); + setContractOnBlur(true); + }} + type="text" + id="contractAdd" + name="contractAddress" + placeholder="NBSE-00x..." + value={contract} + className={ + validContract + ? "contract__input--valid" + : "contract__input--invalid" + } + /> + {contractOnBlur && !validContract && ( + + Error Contract Address + + )} +
+
+ + setTokenId(e.target.value)} + type="text" + id="tokedId" + name="tokenId" + placeholder="01" + value={tokenId} + /> +
+
+
+ Import +
+
+ Cancel +
+
+ +
+
+ ); +} +MultiversexBody.propTypes = { + error: PropTypes.string, + tokenId: PropTypes.string, + handleImport: PropTypes.any, + handleClose: PropTypes.any, + validForm: PropTypes.bool, + importBlocked: PropTypes.bool, + validContract: PropTypes.bool, + setTokenId: PropTypes.any, + setContractOnBlur: PropTypes.any, + contract: PropTypes.string, + contractOnBlur: PropTypes.string, + handleContractChange: PropTypes.any, +}; diff --git a/src/components/NFTsBoard/NFTaccount.jsx b/src/components/NFTsBoard/NFTaccount.jsx index e8d0ad81..79cc9a1c 100755 --- a/src/components/NFTsBoard/NFTaccount.jsx +++ b/src/components/NFTsBoard/NFTaccount.jsx @@ -6,13 +6,10 @@ import { Modal } from "react-bootstrap"; import ImportNFTModal from "../Modals/ImportNFTModal/ImportNFTModal"; import { setBalance, - setError, cleanSelectedNFTList, setBigLoader, - setPreloadNFTs, setNFTList, } from "../../store/reducers/generalSlice"; -import { setIsEmpty } from "../../store/reducers/paginationSlice"; import { useDispatch, useSelector } from "react-redux"; import { saveForSearch } from "../../utils"; import { ReturnBtn } from "../Settings/returnBtn"; @@ -50,12 +47,14 @@ import ReceiverIsContract from "../Alerts/ReceiverIsContract"; const intervalTm = 15_000; function NFTaccount(props) { - const { serviceContainer, chainSpecific, _from, chainSpecificRender } = props; + const { serviceContainer, + // chainSpecific, + _from, chainSpecificRender } = props; const dispatch = useDispatch(); - const from = _from.key; - const secret = from === "Secret"; + // const from = _from.key; + // const secret = from === "Secret"; //const prevSelected = usePrevious(from); let nfts = useSelector((state) => state.general.NFTList); @@ -90,30 +89,30 @@ function NFTaccount(props) { const { bridge } = serviceContainer; - async function getNFTsList(fromChain, contract) { - dispatch(setBigLoader(true)); - try { - let [nfts] = await Promise.all([ - fromChain.getNFTs(bridge.checkWallet || _account, contract), - chainSpecific && chainSpecific(dispatch, fromChain, _account), - ]); + // async function getNFTsList() { + // dispatch(setBigLoader(true)); + // try { + // let [nfts] = await Promise.all([ + // fromChain.getNFTs(bridge.checkWallet || _account, contract), + // chainSpecific && chainSpecific(dispatch, fromChain, _account), + // ]); - nfts = fromChain.filterNFTs(nfts); + // nfts = fromChain.filterNFTs(nfts); - //fromChain.estimateDeployUserStore(); + // //fromChain.estimateDeployUserStore(); - dispatch(setNFTList(nfts)); - dispatch(setPreloadNFTs(nfts.length)); - dispatch(setIsEmpty(nfts.length < 1)); + // dispatch(setNFTList(nfts)); + // dispatch(setPreloadNFTs(nfts.length)); + // dispatch(setIsEmpty(nfts.length < 1)); - dispatch(setBigLoader(false)); - } catch (error) { - dispatch(setBigLoader(false)); - dispatch(setNFTList([])); - console.log(error); - dispatch(setError(error.data ? error.data.message : error.message)); - } - } + // dispatch(setBigLoader(false)); + // } catch (error) { + // dispatch(setBigLoader(false)); + // dispatch(setNFTList([])); + // console.log(error); + // dispatch(setError(error.data ? error.data.message : error.message)); + // } + // } const getBalance = async (fromChain) => { const _balance = await fromChain.balance(_account); @@ -144,7 +143,8 @@ function NFTaccount(props) { if (_account) { getBalance(fromChain); - !secret && getNFTsList(fromChain, preFetchData?.contract); + dispatch(setBigLoader(false)); + dispatch(setNFTList([])); balanceInterval = setInterval(() => getBalance(fromChain), intervalTm); } })(); diff --git a/src/components/NFTsBoard/NFTlistTop.jsx b/src/components/NFTsBoard/NFTlistTop.jsx index aa55d6b6..b6a83e60 100755 --- a/src/components/NFTsBoard/NFTlistTop.jsx +++ b/src/components/NFTsBoard/NFTlistTop.jsx @@ -8,7 +8,6 @@ import { useSelector } from "react-redux"; import ChainListBox from "../Chains/ChainListBox"; import NFTSearch from "./NFTSearch"; import ChainSwitch from "../Buttons/ChainSwitch"; -import Refresh from "../Buttons/Refresh"; import SelectedNFTs from "../Buttons/SelectedNFTs"; import ViewButton from "../Buttons/ViewButton"; import ImportNFTButton from "../Buttons/ImportNFTButton"; @@ -40,21 +39,23 @@ function NFTlistTop({ chainSpecificRender }) { assignment={"from"} func={handleFromChainSwitch} /> - {CheckClaimables && CheckClaimables()}
+ - {from.type === "EVM" && nfts?.length < 1 && } + { + // from.type === "EVM" && + nfts?.length < 1 && } {NFTListTopButton && NFTListTopButton()} {(nfts?.length > 0 || from?.type === "Cosmos") && (
- {from.type === "EVM" && ( - // || - // from?.type !== "Cosmos" + {/* {from.type === "EVM" && ( */} + {/* // || + // from?.type !== "Cosmos" */} - )} + {/* )} */} {/* {onlyWhiteListedNFTs?.length === selectedNFTs?.length && selectedNFTs?.length ? ( diff --git a/src/components/NFTsBoard/NFTmobileView.jsx b/src/components/NFTsBoard/NFTmobileView.jsx index 10993f5d..b0fa195f 100755 --- a/src/components/NFTsBoard/NFTmobileView.jsx +++ b/src/components/NFTsBoard/NFTmobileView.jsx @@ -12,7 +12,6 @@ import { useDispatch, useSelector } from "react-redux"; import MobileDestinationAddressBar from "../MobileOnly/MobileDestinationAddressBar"; import "./NFTsBoard.css"; -import Refresh from "../Buttons/Refresh"; import ChainSwitch from "../Buttons/ChainSwitch"; import SelectedNFTs from "../Buttons/SelectedNFTs"; import ViewButton from "../Buttons/ViewButton"; @@ -71,7 +70,6 @@ const NFTmobileView = ({ selectedNFTs, _from, nfts }) => {
Your NFTs on
-
@@ -89,7 +87,7 @@ const NFTmobileView = ({ selectedNFTs, _from, nfts }) => { showSelected={showSelected} setOff={setShowSelected} /> - {_from.type === "EVM" && nfts?.length < 1 && ( + {_from.type === "EVM" && (nfts?.length === undefined || nfts?.length < 1) && ( )} {nfts?.length > 0 && ( diff --git a/src/event/assets/abi/erc1155Abi.json b/src/event/assets/abi/erc1155Abi.json new file mode 100644 index 00000000..16d6bf6f --- /dev/null +++ b/src/event/assets/abi/erc1155Abi.json @@ -0,0 +1,25 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "id", "type": "uint256" } + ], + "name": "balanceOf", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "id", "type": "uint256" } + ], + "name": "uri", + "outputs": [ + { "internalType": "string", "name": "", "type": "string" } + ], + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/src/event/assets/abi/mintAbi.json b/src/event/assets/abi/mintAbi.json index 8ddca462..bbe876e8 100644 --- a/src/event/assets/abi/mintAbi.json +++ b/src/event/assets/abi/mintAbi.json @@ -325,6 +325,25 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "uri", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "baseUri", @@ -786,4 +805,4 @@ "stateMutability": "view", "type": "function" } -] +] \ No newline at end of file diff --git a/src/models/chains.js b/src/models/chains.js index 67ed0141..48dd6dca 100755 --- a/src/models/chains.js +++ b/src/models/chains.js @@ -782,8 +782,8 @@ class TON extends AbstractChain { async preParse(nft) { const _contract = nft.collectionIdent || "SingleNFt"; - const withMetadata = Object.keys(nft.native?.metadata).length > 0; - let uri = ""; + const withMetadata = Object.keys(nft.native?.metaData ?? {}).length > 0; + let uri = nft?.uri ?? ""; let native_metadata = {}; if (withMetadata) { const data = JSON.stringify(nft.native.metadata); @@ -799,6 +799,7 @@ class TON extends AbstractChain { return { collectionIdent: _contract, uri, + image: nft.image, metaData: withMetadata ? { ...nft.native.metadata, @@ -808,7 +809,7 @@ class TON extends AbstractChain { native: { ...nft.native, - tokenId: nft.native.nftItemAddr, + tokenId: nft?.native?.nftItemAddr ?? nft.native.name?.split("#")?.[1], contract: _contract, }, }; diff --git a/src/utils/chainsTypes.js b/src/utils/chainsTypes.js index 88df786a..603ea36b 100644 --- a/src/utils/chainsTypes.js +++ b/src/utils/chainsTypes.js @@ -129,6 +129,7 @@ export const v3_ChainId = { 7: {name: 'MATIC', type: "EVM"}, 18: {name: 'TEZOS', type: "TEZOS"}, 24: {name: 'SECRET', type: "COSMOS"}, + 26: {name: 'SOLANA', type: "SOLANA"}, 27: {name: 'TON', type: "TON"}, 29: {name: 'HEDERA', type: "HEDERA"}, } diff --git a/src/utils/importNFTUtility.js b/src/utils/importNFTUtility.js new file mode 100755 index 00000000..a630fa1e --- /dev/null +++ b/src/utils/importNFTUtility.js @@ -0,0 +1,504 @@ +import axios from "axios"; +import { ethers } from "ethers"; + +import store from "../store/store"; +import ABI from "../event/assets/abi/mintAbi.json"; +import ABI1155 from "../event/assets/abi/erc1155Abi.json"; +import { setupURI } from "../utils"; + +// ---------------------------- + +// ---------------------------- + +/** + * FORMAT DATA + */ +const formatData = ( + contractAddress, + uri, + owner, + tokenId, + contractType, + chainId, + native +) => { + const obj = { + collectionIdent: contractAddress, + uri, + native: { + owner, + tokenId, + uri, + contract: contractAddress, + chainId, + contractType, + ...native, + }, + }; + + if (native?.image) { + obj.image = native.image; + } + + return obj; +}; + +/** + * CHECK VALID FORM + */ +export const validForm = (from, contract, tokenId) => { + switch (from.type) { + case "EVM": + return contract?.length === 42 && tokenId; + case "Elrond": + return tokenId; + case "Hedera": + case "Tezos": + return contract && tokenId; + case "TON": + return contract && contract.length > 10; + default: + return true; + } +}; + +/** + * IMPORT INPUTS + */ +export const importInputs = (from) => { + switch (from.type) { + case "EVM": + return { + contract: { + label: "1. Paste contract address", + placeholder: "0x...", + }, + tokenId: { + label: "2. Paste Token ID", + placeholder: "Enter Token ID", + }, + }; + case "Elrond": + return { + contract: { + label: "1. Paste Collection address", + placeholder: "0x...", + }, + tokenId: { + label: "2. Paste Token ID", + placeholder: "Enter Token ID", + }, + }; + case "Hedera": + return { + contract: { + label: "1. Paste contract address", + placeholder: "EBSD-...", + }, + tokenId: { + label: "2. Paste Token ID", + placeholder: "01", + }, + }; + case "Tezos": + return { + contract: { + label: "1. Paste contract address", + placeholder: "0x...", + }, + tokenId: { + label: "2. Paste Token ID", + placeholder: "Enter Token ID", + }, + }; + case "TON": + return { + contract: { + label: "1. Paste item address", + placeholder: "EQD...", + }, + tokenId: { + label: "", + placeholder: "", + }, + }; + case "Solana": + return { + contract: { + label: "1. Paste Token address", + placeholder: "D0...", + }, + tokenId: { + label: "", + placeholder: "", + }, + }; + + default: + return { + contract: { + label: "1. Paste contract address", + placeholder: "0x...", + }, + tokenId: { + label: "2. Paste Token ID", + placeholder: "Enter Token ID", + }, + }; + } +}; + +/** + * CHECK NFT EXIST + */ +export const checkNFTExist = (NFTList, contractAddress, tokenId, from) => { + switch (from.type) { + case "Elrond": + return NFTList.find((n) => + n.native.contract === contractAddress && + n.native.tokenId == tokenId.toString().length < 2 + ? "0" + tokenId.toString() + : tokenId.toString() + ); + case "TON": + case "Solana": + return NFTList.find((n) => n.native.contract === contractAddress); + default: + return NFTList.find( + (n) => + n.native.contract === contractAddress && + n.native.tokenId == tokenId?.toString() + ); + } +}; + +/** + * IMPORT NFT URI + */ +export const importNFTURI = async ( + contract, + tokenId, + account, + signer, + from +) => { + switch (from.type) { + case "EVM": + return await importNFTURI_EVM(contract, tokenId, account, signer, from); + case "Elrond": + return await importNFTURI_Elrond(contract, tokenId, account, from); + case "Hedera": + return await importNFTURI_Hedera(contract, tokenId, account, from); + case "Tezos": + return await importNFTURI_Tezos(contract, tokenId, account, from); + case "TON": + return await importNFTURI_TON(contract, account, from); + case "Solana": + return await importNFTURI_Solana(contract, account, from); + default: + throw new Error("Invalid chain"); + } +}; + +/** + * IMPORT NFT URI FROM EVM CHAIN + */ +export const importNFTURI_EVM = async ( + contract, + tokenId, + account, + signer, + from +) => { + const Contract721 = new ethers.Contract(contract, ABI, signer); + const Contract1155 = new ethers.Contract(contract, ABI1155, signer); + + try { + // EVM CHAIN for ERC721 + const owner721 = await Contract721.ownerOf(tokenId); + if (owner721 !== account) throw new Error("You don't own this NFT!"); + + const tokenURI = await Contract721.tokenURI(tokenId); + return formatData( + contract, + tokenURI, + account, + tokenId, + "ERC721", + from.chainId + ); + } catch (error) { + console.log("Error 721", error); + try { + // CHECK IF THE USER HAS THE NFT ON ERC1155 CHAIN + const balance1155 = await Contract1155.balanceOf(account, tokenId); + if (balance1155 <= 0) throw new Error("You don't own this NFT!"); + + const tokenURI = await Contract1155.uri(tokenId); + return formatData( + contract, + tokenURI, + account, + tokenId, + "ERC1155", + from.chainId + ); + } catch (error) { + console.log({ error }); + throw new Error("You don't own this NFT!"); + } + } +}; + +/** + * IMPORT NFT URI FROM ELROND CHAIN + */ +export const importNFTURI_Elrond = async (contract, tokenId, account, from) => { + try { + const { + general: { testNet }, + } = store.getState(); + const id = tokenId.toString(); + const fomatedID = id.length < 2 ? "0" + id : id; + const tokenIdentifier = `${contract}-${fomatedID}`; + const { data } = await axios.get( + testNet + ? `https://devnet-api.multiversx.com/nfts/${tokenIdentifier}` + : `https://api.multiversx.com/nfts/${tokenIdentifier}` + ); + + if (data.owner !== account) { + return Promise.reject(new Error("You don't own this NFT!")); + } + + return formatData(contract, data.url, account, fomatedID, "", from.nonce, { + ...data, + image: data.url, + }); + } catch (error) { + console.log({ error }); + throw new Error( + error.response?.data?.message || + error.message || + "An error occurred while importing the NFT!" + ); + } +}; + +/** + * IMPORT NFT FROM HEDERA CHAIN + */ +export const importNFTURI_Hedera = async ( + tokenId, + serialNumber, + account, + from +) => { + try { + const { + general: { testNet }, + } = store.getState(); + const { data } = await axios.get( + testNet + ? `https://testnet.mirrornode.hedera.com/api/v1/tokens/${tokenId}/nfts/${serialNumber}` + : `https://mainnet.mirrornode.hedera.com/api/v1/tokens/${tokenId}/nfts/${serialNumber}` + ); + + if (data.account_id !== account) { + return Promise.reject(new Error("You don't own this NFT!")); + } + + const decodeMetadata = await Buffer.from( + data.metadata, + "base64" + ).toLocaleString(); + + const { data: metadata } = await axios.get(setupURI(decodeMetadata)); + + return formatData( + tokenId, + metadata.imageUrl, + account, + serialNumber, + "", + from.nonce, + metadata + ); + } catch (error) { + console.log({ error }); + throw new Error( + error.response?.data?.message || + error.message || + "An error occurred while importing the NFT!" + ); + } +}; + +/** + * IMPORT NFT FROM TEZOS CHAIN + */ +export const importNFTURI_Tezos = async (contract, tokenId, account, from) => { + try { + const { + general: { testNet }, + } = store.getState(); + const { data } = await axios.get( + testNet + ? `https://api.ghostnet.tzkt.io/v1/tokens/balances?account=${account}&token.tokenId=${tokenId}&token.contract=${contract}&token.standard=fa2&limit=10000` + : `https://api.tzkt.io/v1/tokens/balances?account=${account}&token.tokenId=${tokenId}&token.contract=${contract}&token.standard=fa2&limit=10000` + ); + + const { token } = data.find((t) => t.token.tokenId === tokenId); + + if (data.length <= 0 || !token) { + return Promise.reject(new Error("You don't own this NFT!")); + } + + return formatData( + contract, + setupURI(token.metadata?.artifactUri), + account, + tokenId, + "FA2", + from.nonce, + { + ...token.metadata, + image: setupURI( + token.metadata?.displayUri ?? token.metadata?.artifactUri + ), + animation_url: setupURI(token.metadata?.artifactUri), + } + ); + } catch (error) { + console.log({ error }); + throw new Error( + error.response?.data?.message || + error.message || + "An error occurred while importing the NFT!" + ); + } +}; + +/** + * IMPORT NFT FROM TON CHAIN + */ +export const importNFTURI_TON = async (contract, account, from) => { + try { + const { + general: { testNet }, + } = store.getState(); + const { data } = await axios.get( + testNet + ? `https://testnet.toncenter.com/api/v3/nft/items?address=${contract}` + : `https://toncenter.com/api/v3/nft/items?address=${contract}` + ); + + if (data.nft_items?.length === 0) { + return Promise.reject(new Error("Invalid item address!")); + } + + const item = data.nft_items[0]; + + if (item.owner_address !== account) { + return Promise.reject(new Error("You don't own this NFT!")); + } + + const metadata = await axios.get(setupURI(item.content.uri)); + + return formatData( + contract, + setupURI(item.content.uri), + account, + contract, + "TON", + from.nonce, + { + ...metadata.data, + image: metadata.data.image, + } + ); + } catch (error) { + console.log({ error }); + throw new Error( + error.response?.data?.message || + error.message || + "An error occurred while importing the NFT!" + ); + } +}; + +/** + * IMPORT NFT FROM SOLANA CHAIN + */ +export const importNFTURI_Solana = async (contract, account, from) => { + try { + const { + general: { testNet }, + } = store.getState(); + const { data } = await axios.post( + testNet + ? "https://explorer-api.devnet.solana.com/" + : "https://explorer-api.mainnet-beta.solana.com/", + { + id: contract, + jsonrpc: "2.0", + method: "getAsset", + params: { + id: contract, + }, + } + ); + + if (data.error) { + return Promise.reject( + new Error( + data.error.code === -32000 + ? "Invalid contract address!" + : "An error occurred while importing the NFT!" + ) + ); + } + + if (data?.result?.authorities?.[0]?.address !== account) { + return Promise.reject(new Error("You don't own this NFT!")); + } + + const { data: metadata } = await axios.get(data.result.content.json_uri); + + console.log( + formatData( + contract, + metadata.image, + account, + contract, + "SPL", + from.nonce, + { + ...metadata, + image: metadata.image, + } + ) + ); + + return formatData( + contract, + metadata.image, + account, + contract, + "SPL", + from.nonce, + { + ...metadata, + image: metadata.image, + nftMint: contract, + } + ); + } catch (error) { + console.log({ error }); + throw new Error( + error.response?.data?.message || + error.message || + "An error occurred while importing the NFT!" + ); + } +};