diff --git a/src/assets/ethereum.svg b/src/assets/ethereum.svg new file mode 100644 index 00000000..ce19a24f --- /dev/null +++ b/src/assets/ethereum.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/video/infinite-initiate.mp4 b/src/assets/video/infinite-initiate.mp4 new file mode 100644 index 00000000..86578d28 Binary files /dev/null and b/src/assets/video/infinite-initiate.mp4 differ diff --git a/src/contracts/erc1155Abi.ts b/src/contracts/erc1155Abi.ts new file mode 100644 index 00000000..a8df8a76 --- /dev/null +++ b/src/contracts/erc1155Abi.ts @@ -0,0 +1 @@ +export const erc1155Abi = [{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AlreadyAdmin","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"ArrayLengthsMismatch","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidAdminAddress","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NotAdmin","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"OwnerAlreadyAdmin","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferToNonERC1155ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"AdminAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"}],"name":"AdminRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokenMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"string","name":"newURI","type":"string"}],"name":"URISet","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"URIs","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"addAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"admins","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"mintBatch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"removeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"string","name":"newURI","type":"string"}],"name":"setURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokenExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}] as const; \ No newline at end of file diff --git a/src/contracts/minterAbi.ts b/src/contracts/minterAbi.ts new file mode 100644 index 00000000..e2a9057b --- /dev/null +++ b/src/contracts/minterAbi.ts @@ -0,0 +1,405 @@ + +export const minterAbi = [ + { + "inputs":[ + { + "internalType":"address", + "name":"_nftContract", + "type":"address" + }, + { + "internalType":"address", + "name":"_sastToken", + "type":"address" + } + ], + "stateMutability":"nonpayable", + "type":"constructor" + }, + { + "inputs":[], + "name":"AlreadyMinted", + "type":"error" + }, + { + "inputs":[], + "name":"InsufficientSASTBalance", + "type":"error" + }, + { + "inputs":[], + "name":"InvalidMintQuantity", + "type":"error" + }, + { + "inputs":[], + "name":"InvalidTokenAddress", + "type":"error" + }, + { + "inputs":[], + "name":"Unauthorized", + "type":"error" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "internalType":"uint256", + "name":"oldQuantity", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"newQuantity", + "type":"uint256" + } + ], + "name":"MintQuantityUpdated", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "internalType":"uint256", + "name":"oldTokenId", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"newTokenId", + "type":"uint256" + } + ], + "name":"MintableTokenIdUpdated", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"user", + "type":"address" + }, + { + "indexed":true, + "internalType":"uint256", + "name":"tokenId", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"quantity", + "type":"uint256" + } + ], + "name":"NFTMinted", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "internalType":"uint256", + "name":"oldBalance", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"newBalance", + "type":"uint256" + } + ], + "name":"RequiredBalanceUpdated", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"oldAddress", + "type":"address" + }, + { + "indexed":true, + "internalType":"address", + "name":"newAddress", + "type":"address" + } + ], + "name":"SASTTokenUpdated", + "type":"event" + }, + { + "inputs":[ + { + "internalType":"address[]", + "name":"users", + "type":"address[]" + } + ], + "name":"batchMintNFTs", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"user", + "type":"address" + } + ], + "name":"canMint", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"getTotalTokensMinted", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"user", + "type":"address" + } + ], + "name":"getUserSASTBalance", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "name":"hasMinted", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"mintNFT", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[], + "name":"mintQuantity", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"mintableTokenId", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"nftContract", + "outputs":[ + { + "internalType":"contract IAirswapNFT", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"owner", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"requiredSASTBalance", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"sastToken", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[], + "name":"totalMinted", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "stateMutability":"view", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"_newOwner", + "type":"address" + } + ], + "name":"transferOwnership", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"uint256", + "name":"_mintQuantity", + "type":"uint256" + } + ], + "name":"updateMintQuantity", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"uint256", + "name":"_mintableTokenId", + "type":"uint256" + } + ], + "name":"updateMintableTokenId", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"uint256", + "name":"_requiredBalance", + "type":"uint256" + } + ], + "name":"updateRequiredBalance", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + }, + { + "inputs":[ + { + "internalType":"address", + "name":"_sastToken", + "type":"address" + } + ], + "name":"updateSASTToken", + "outputs":[ + + ], + "stateMutability":"nonpayable", + "type":"function" + } +] as const; diff --git a/src/features/claims/ClaimForm.tsx b/src/features/claims/ClaimSelectionForm.tsx similarity index 99% rename from src/features/claims/ClaimForm.tsx rename to src/features/claims/ClaimSelectionForm.tsx index 1c56d7ce..ae7edd6c 100644 --- a/src/features/claims/ClaimForm.tsx +++ b/src/features/claims/ClaimSelectionForm.tsx @@ -23,8 +23,9 @@ import { import { useClaimableAmounts } from "./hooks/useClaimableAmounts"; import { useResetClaimStatus } from "./hooks/useResetClaimStatus"; -export const ClaimForm = ({}: {}) => { +export const ClaimStakingRewardForm = ({}: {}) => { const [pool] = useContractAddresses([ContractTypes.AirSwapPool], {}); + const { address: connectedAccount } = useAccount(); const [withdrawnAmount, setWithdrawnAmount] = useState(); diff --git a/src/features/common/utils/formatNumber.ts b/src/features/common/utils/formatNumber.ts index 71475721..2f681713 100644 --- a/src/features/common/utils/formatNumber.ts +++ b/src/features/common/utils/formatNumber.ts @@ -3,6 +3,7 @@ import BigNumber from "bignumber.js"; export const formatNumber = ( number?: number | bigint, decimals: number | null = null, + options?: (Intl.NumberFormatOptions & BigIntToLocaleStringOptions), ) => { if (number == null) return number; @@ -21,5 +22,6 @@ export const formatNumber = ( minimumSignificantDigits: 3, maximumSignificantDigits: 3, notation: "compact", + ...options, }); }; diff --git a/src/features/rewards/ClaimNftButton.tsx b/src/features/rewards/ClaimNftButton.tsx new file mode 100644 index 00000000..f3c3ef4d --- /dev/null +++ b/src/features/rewards/ClaimNftButton.tsx @@ -0,0 +1,67 @@ +import { twJoin } from "tailwind-merge"; +import { useAccount } from "wagmi"; +import { useTokenBalances } from "../../hooks/useTokenBalances"; +import { Button } from "../common/Button"; +import { ClaimNftRewardForm } from "./ClaimNftRewardForm"; +import { useState } from "react"; +import { Modal } from "../common/Modal"; +import { requiredBalanceForNftClaim } from "./config"; +import { formatNumber } from "../common/utils/formatNumber"; +import { CheckMark } from "../common/icons/CheckMark"; +import { MdClose } from "react-icons/md"; + +export const ClaimNftButton = () => { + const { isConnected } = useAccount(); + const [showClaimNftModal, setShowClaimNftModal] = useState(false); + const { sAstBalanceRaw, sAstBalanceV4_DeprecatedRaw } = useTokenBalances(); + + const totalSastBalance = sAstBalanceRaw + sAstBalanceV4_DeprecatedRaw; + const formattedRequiredBalance = formatNumber(requiredBalanceForNftClaim, 4, { minimumSignificantDigits: 4, maximumSignificantDigits: 4 }); + // const isBalanceEnough = !!totalSastBalance && totalSastBalance >= requiredBalanceForNftClaim; + const isBalanceEnough = true; + const isEligible = isConnected && isBalanceEnough; + + return ( + <> + {isEligible && ( +
+ + Free AirSwap NFT + + + +
+ )} + + {showClaimNftModal && ( + setShowClaimNftModal(false)} + heading={isEligible ? "Eligible for free mint" : "Not eligible for free mint"} + subHeading={ +
+ {`Required: ${formattedRequiredBalance} sAST`} + + {isEligible ? : } + +
+ } + > + +
+ )} + + ); +}; diff --git a/src/features/rewards/ClaimNftRewardForm.tsx b/src/features/rewards/ClaimNftRewardForm.tsx new file mode 100644 index 00000000..92e885f5 --- /dev/null +++ b/src/features/rewards/ClaimNftRewardForm.tsx @@ -0,0 +1,125 @@ +import { useEffect } from "react"; +import { + useAccount, + useChainId, + useContractWrite, + usePrepareContractWrite, + usePublicClient, + useWaitForTransaction, +} from "wagmi"; +import { ContractTypes } from "../../config/ContractAddresses"; +import { useContractAddresses } from "../../config/hooks/useContractAddress"; +import { minterAbi } from "../../contracts/minterAbi"; +import { useTokenBalances } from "../../hooks/useTokenBalances"; +import { Button } from "../common/Button"; +import { TransactionTracker } from "../common/TransactionTracker"; +import { requiredBalanceForNftClaim } from "./config"; +import { useNftInfo } from "./hooks/useNftInfo"; +import { InfiniteInitiate } from "./InfiniteInitiate"; +import { useClaimNftStore } from "./store/useClaimNftStore"; + +const nftAddress = "0xf80cd411d49804d4a80bfe3b26dad4679b7918d7"; + +export const ClaimNftRewardForm = () => { + const [pool] = useContractAddresses([ContractTypes.AirSwapPool], {}); + const { address: connectedAccount } = useAccount(); + + const chainId = useChainId(); + const publicClient = usePublicClient({ chainId: chainId }); + + const { sAstBalanceRaw, sAstBalanceV4_DeprecatedRaw } = useTokenBalances(); + + const totalSastBalance = sAstBalanceRaw + sAstBalanceV4_DeprecatedRaw; + const isBalanceEnough = + !!totalSastBalance && totalSastBalance >= requiredBalanceForNftClaim; + + const [showClaimNftModal, setShowClaimNftModal, setIsClaimLoading] = + useClaimNftStore((state) => [ + state.showClaimNftModal, + state.setShowClaimNftModal, + state.setIsClaimLoading, + ]); + + const { data: nftInfo } = useNftInfo({ address: nftAddress, id: 0n }); + + const { config: mintTxConfig } = usePrepareContractWrite({ + ...pool, + abi: minterAbi, + functionName: "mintNFT", + enabled: isBalanceEnough, + }); + + const { + data: writeResult, + write, + reset: resetContractWrite, + isLoading: waitingForSignature, + } = useContractWrite({ + ...mintTxConfig, + onSuccess: async (result) => { + const receipt = await publicClient.waitForTransactionReceipt({ + hash: result.hash, + }); + + // Show claim success + }, + onError: (e: any) => { + if (e?.cause?.code === 4001) { + // Do nothing here, the user rejected the tx + } else { + // Show claim failed + } + }, + }); + + const { status: txStatus } = useWaitForTransaction({ + hash: writeResult?.hash, + }); + + const actionButtons = { + afterFailure: { + label: "Try again", + callback: () => { + resetContractWrite(); + }, + }, + afterSuccess: { + label: "Close", + callback: () => { + setShowClaimNftModal(false); + }, + }, + }; + + useEffect(() => { + if (txStatus === "loading" || waitingForSignature) { + setIsClaimLoading(true); + } else { + setIsClaimLoading(false); + } + }, [txStatus, setIsClaimLoading, waitingForSignature]); + + return writeResult?.hash || waitingForSignature ? ( + + ) : ( +
+ + + +
+ ); +}; diff --git a/src/features/rewards/InfiniteInitiate.tsx b/src/features/rewards/InfiniteInitiate.tsx new file mode 100644 index 00000000..d0c7c944 --- /dev/null +++ b/src/features/rewards/InfiniteInitiate.tsx @@ -0,0 +1,22 @@ +import { GoLinkExternal } from "react-icons/go"; +import infiniteInitiate from "../../assets/video/infinite-initiate.mp4" +import ethereumLogo from "../../assets/ethereum.svg" +import { mainnet } from "wagmi"; + +const url = "https://sepolia.etherscan.io/token/0xf80cd411d49804d4a80bfe3b26dad4679b7918d7"; + +export const InfiniteInitiate = ({className}: {className?: string}) => { + return ( +
+

Infinite Initiate

+
+ Ethereum + ERC-1150 + + + +
+
+ ) +}; \ No newline at end of file diff --git a/src/features/rewards/config.ts b/src/features/rewards/config.ts new file mode 100644 index 00000000..c9c75502 --- /dev/null +++ b/src/features/rewards/config.ts @@ -0,0 +1 @@ +export const requiredBalanceForNftClaim = 10010000n; diff --git a/src/features/rewards/hooks/useNftInfo.ts b/src/features/rewards/hooks/useNftInfo.ts new file mode 100644 index 00000000..bff03bd8 --- /dev/null +++ b/src/features/rewards/hooks/useNftInfo.ts @@ -0,0 +1,48 @@ +import { useQuery } from "@tanstack/react-query"; +import { Address, useChainId, useContractRead } from "wagmi"; +import { erc1155Abi } from "../../../contracts/erc1155Abi"; + +type AirSwapNftInfo = { + created_by: string; + description: string; + image: string; + name: string; +}; + +export const useNftInfo = ({ + address: nftAddress, + id, +}: { + address: Address; + id: bigint; +}) => { + const chainId = useChainId(); + + const { data: uri } = useContractRead({ + address: nftAddress, + abi: erc1155Abi, + chainId, + functionName: "uri", + args: [id], + }); + + return useQuery({ + queryKey: ["nftInfo", nftAddress, id], + queryFn: async () => { + // Handle IPFS URLs (convert ipfs:// to https://ipfs.io/ipfs/) + let url = uri as string; + if (url.startsWith("ipfs://")) { + url = url.replace("ipfs://", "https://ipfs.io/ipfs/"); + } + + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch metadata: ${response.status}`); + } + + const jsonData = await response.json(); + return jsonData; + }, + enabled: !!uri, + }); +}; diff --git a/src/features/rewards/store/useClaimNftStore.ts b/src/features/rewards/store/useClaimNftStore.ts new file mode 100644 index 00000000..07294a26 --- /dev/null +++ b/src/features/rewards/store/useClaimNftStore.ts @@ -0,0 +1,32 @@ +import { create } from "zustand"; +import { persist } from "zustand/middleware"; + +export type ClaimNftState = { + /** Whether or not we are showing the claim modal */ + showClaimNftModal: boolean; + setShowClaimNftModal: (show: boolean) => void; + isClaimLoading: boolean; + setIsClaimLoading: (isClaimLoading: boolean) => void; +} + +const defaultState = { + showClaimNftModal: false, + isClaimLoading: false, +} + +export const useClaimNftStore = create()( + persist( + (set) => ({ + ...defaultState, + setShowClaimNftModal(show: boolean) { + set({ showClaimNftModal: show }); + }, + setIsClaimLoading(isClaimLoading: boolean) { + set({ isClaimLoading }); + }, + }), + { + name: "claimNftStore", + }, + ), +); \ No newline at end of file diff --git a/src/features/structure/Header.tsx b/src/features/structure/Header.tsx index 293b13b1..9baf2aef 100644 --- a/src/features/structure/Header.tsx +++ b/src/features/structure/Header.tsx @@ -3,6 +3,7 @@ import AirSwapLogoWithText from "../../assets/airswap-logo-with-text.svg"; import AirSwapLogo from "../../assets/airswap-logo.svg"; import WalletConnection from "../chain-connection/WalletConnection"; import { StakingButton } from "../staking/StakingButton"; +import { ClaimNftButton } from "../rewards/ClaimNftButton"; export const Header = ({}: {}) => { const { isConnected } = useAccount(); @@ -24,8 +25,9 @@ export const Header = ({}: {}) => {
+ {isConnected && } - {isConnected ? : null} + {isConnected && }
); diff --git a/src/features/votes/VoteList.tsx b/src/features/votes/VoteList.tsx index 650fa364..95f0705e 100644 --- a/src/features/votes/VoteList.tsx +++ b/src/features/votes/VoteList.tsx @@ -2,7 +2,7 @@ import { Fragment } from "react"; import { useAccount, useChainId } from "wagmi"; import { ActivatePointsCard } from "../activate-migration/ActivatePointsCard"; import { NON_MAINNET_START_TIMESTAMP } from "../activate-migration/constants"; -import { ClaimForm } from "../claims/ClaimForm"; +import { ClaimStakingRewardForm } from "../claims/ClaimSelectionForm"; import { ClaimModalSubheading } from "../claims/ClaimModalSubheading"; import { CustomTokensForm } from "../claims/CustomTokensForm"; import { Modal } from "../common/Modal"; @@ -134,9 +134,10 @@ export const VoteList = ({}: {}) => { subHeading={} className={`${showCustomTokensModal ? "hidden" : ""}`} > - + )} + {showCustomTokensModal && ( setShowCustomTokensModal(false)}