From eedad81d6dd8a1b512a46de61c9614ab5404fcf0 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 24 Aug 2023 21:10:06 -0400 Subject: [PATCH 01/44] init --- src/components/cards/GasCard.tsx | 116 ++- src/components/contacts/ContactAvatar.js | 7 +- .../transactions/transactionStatus.ts | 4 + src/entities/transactions/transactionType.ts | 2 + src/graphql/config.js | 3 +- src/graphql/index.ts | 2 +- src/graphql/queries/arc.graphql | 40 + src/handlers/transactions.ts | 2 + src/helpers/transactions.ts | 2 + src/navigation/Routes.ios.js | 6 + src/navigation/routesNames.js | 1 + src/parsers/transactions.ts | 13 + src/resources/nftOffers/utils.ts | 10 +- .../PairHardwareWalletAgainSheet.tsx | 15 +- src/screens/mints/MintSheet.tsx | 796 ++++++++++++++++++ .../mints/components/QuantityButton.tsx | 163 ++++ 16 files changed, 1101 insertions(+), 81 deletions(-) create mode 100644 src/screens/mints/MintSheet.tsx create mode 100644 src/screens/mints/components/QuantityButton.tsx diff --git a/src/components/cards/GasCard.tsx b/src/components/cards/GasCard.tsx index fa1f34ab330..5712d5c8ad7 100644 --- a/src/components/cards/GasCard.tsx +++ b/src/components/cards/GasCard.tsx @@ -2,7 +2,7 @@ import AnimateNumber from '@bankify/react-native-animate-number'; import { useIsFocused } from '@react-navigation/native'; import * as i18n from '@/languages'; -import { isNaN } from 'lodash'; +import { isNaN, truncate } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Animated, { Easing, @@ -12,6 +12,8 @@ import Animated, { withSpring, withTiming, } from 'react-native-reanimated'; +import Routes from '@/navigation/routesNames'; + import { AccentColorProvider, Box, @@ -20,15 +22,33 @@ import { Text, } from '@/design-system'; import { add } from '@/helpers/utilities'; -import { useGas } from '@/hooks'; +import { useAccountSettings } from '@/hooks'; import { gasUtils } from '@/utils'; import { GenericCard, SQUARE_CARD_SIZE } from './GenericCard'; +import { arcClient, arcDevClient } from '@/graphql'; +import { getZoraNetworkObject } from '@/networks/zora'; +import { useNavigation } from '@/navigation'; +import { getOptimismNetworkObject } from '@/networks/optimism'; type AnimationConfigOptions = { duration: number; easing: Animated.EasingFunction; }; +const mints = { + OP: { + addy: '0xcB0Bb5D835A47584fdA53F57bb4193B28d2738dB', + chain: getOptimismNetworkObject().id + }, + Zora: { + addy: '0x81eCE04F2aFadCfb1c06f8331de8cba253564Ec1', + chain: getZoraNetworkObject().id + }, + Eth: { + addy: '0x932261f9Fc8DA46C4a22e31B45c4De60623848bF', + chain: 1 + } +} const TRANSLATIONS = i18n.l.cards.gas; const containerConfig = { @@ -47,56 +67,34 @@ const fadeOutConfig: AnimationConfigOptions = { }; export const GasCard = () => { - const { - currentBlockParams, - gasFeeParamsBySpeed, - startPollingGasFees, - stopPollingGasFees, - } = useGas(); const isFocused = useIsFocused(); const [lastKnownGwei, setLastKnownGwei] = useState(''); + const { accountAddress} = useAccountSettings(); + const { navigate} = useNavigation(); const container = useSharedValue(1); const opacity = useSharedValue(0); const scale = useSharedValue(0); - // Listen to gas prices - useEffect(() => { - if (isFocused) { - startPollingGasFees(); - } else { - stopPollingGasFees(); - } - }, [isFocused, startPollingGasFees, stopPollingGasFees]); + const { NORMAL } = gasUtils; - const currentGwei = useMemo( - () => - add( - currentBlockParams?.baseFeePerGas?.gwei, - gasFeeParamsBySpeed[NORMAL]?.maxPriorityFeePerGas?.gwei - ), - [NORMAL, currentBlockParams?.baseFeePerGas?.gwei, gasFeeParamsBySpeed] - ); - const isCurrentGweiLoaded = currentGwei && Number(currentGwei) > 0; + + const renderGweiText = useCallback( // @ts-expect-error passed to an untyped JS component animatedNumber => { const priceText = - animatedNumber === 0 - ? isCurrentGweiLoaded - ? Math.round(Number(currentGwei)) - : Math.round(Number(lastKnownGwei)) || '􀖇' - : animatedNumber; + 'lol' return ( {priceText} ); }, - [currentGwei, isCurrentGweiLoaded, lastKnownGwei] + [] ); // @ts-expect-error passed to an untyped JS component @@ -108,24 +106,21 @@ export const GasCard = () => { } }, []); - const handlePress = useCallback(() => { - opacity.value = 0; - scale.value = 0; - container.value = withSequence( - withSpring(1.04, containerConfig), - withSpring(1, pulseConfig) - ); - opacity.value = withSequence( - withSpring(1, pulseConfig), - withTiming(0, fadeOutConfig), - withTiming(0, { duration: 0 }) - ); - scale.value = withSequence( - withSpring(1, pulseConfig), - withTiming(1, fadeOutConfig), - withTiming(0, { duration: 0 }) - ); - }, [container, opacity, scale]); + const handlePress = useCallback(async () => { + try{ + navigate(Routes.HARDWARE_WALLET_TX_NAVIGATOR); + // navigate(Routes.NOTIFICATIONS_PROMO_SHEET); + // const res = await arcDevClient.getSingleCollection({walletAddress: accountAddress, contractAddress: mints.Zora.addy, chain: mints.Zora.chain}) + // console.log('posty'); + // console.log({res}); + // navigate(Routes.MINT_SHEET, {collection: res.getSingleCollection?.collection}) + + } catch (e) +{ + console.log(e) +} + + }, [accountAddress, navigate]); const getColorForGwei = (currentGwei: string, lastKnownGwei: string) => { 'worklet'; @@ -169,10 +164,9 @@ export const GasCard = () => { useEffect(() => { if ( - isCurrentGweiLoaded && - Math.round(Number(currentGwei)) !== Math.round(Number(lastKnownGwei)) + true ) { - setLastKnownGwei(currentGwei); + setLastKnownGwei('66'); opacity.value = 0; scale.value = 0; container.value = withSequence( @@ -192,8 +186,6 @@ export const GasCard = () => { } }, [ container, - currentGwei, - isCurrentGweiLoaded, lastKnownGwei, opacity, scale, @@ -207,10 +199,10 @@ export const GasCard = () => { }, ], }; - }, [currentGwei, lastKnownGwei]); + }, [ lastKnownGwei]); const pulseStyle = useAnimatedStyle(() => { - const color = getColorForGwei(currentGwei, lastKnownGwei); + const color = getColorForGwei('66', lastKnownGwei); return { backgroundColor: color, @@ -224,13 +216,13 @@ export const GasCard = () => { ], width: SQUARE_CARD_SIZE, }; - }, [currentGwei, lastKnownGwei]); + }, [ lastKnownGwei]); return ( { interval={2} renderContent={renderGweiText} timing={(t: number) => 1 - --t * t * t * t} - value={currentGwei || lastKnownGwei} + value={'66' || lastKnownGwei} /> - {!isCurrentGweiLoaded && !lastKnownGwei + { !lastKnownGwei ? '' : i18n.t(TRANSLATIONS.gwei)} @@ -259,14 +251,14 @@ export const GasCard = () => { - {getCurrentPriceComparison(currentGwei, lastKnownGwei)} + {getCurrentPriceComparison('66', lastKnownGwei)} diff --git a/src/components/contacts/ContactAvatar.js b/src/components/contacts/ContactAvatar.js index af099895c4c..68af91bfc10 100644 --- a/src/components/contacts/ContactAvatar.js +++ b/src/components/contacts/ContactAvatar.js @@ -118,12 +118,7 @@ const ContactAvatar = ({ color, size = 'medium', value, ...props }) => { ]); const { isDarkMode } = useTheme(); - const shadows = useMemo(() => buildShadows(color, size, isDarkMode, colors), [ - color, - size, - isDarkMode, - colors, - ]); + const shadows = useMemo(() => buildShadows(color, size, props?.forceDarkMode || isDarkMode, colors), [color, size, props?.forceDarkMode, isDarkMode, colors]); const backgroundColor = typeof color === 'number' diff --git a/src/entities/transactions/transactionStatus.ts b/src/entities/transactions/transactionStatus.ts index f21b112bd72..0e15919e55f 100644 --- a/src/entities/transactions/transactionStatus.ts +++ b/src/entities/transactions/transactionStatus.ts @@ -10,6 +10,8 @@ export enum TransactionStatus { depositing = 'depositing', dropped = 'dropped', failed = 'failed', + minted = 'minted', + minting = 'minting', purchased = 'purchased', purchasing = 'purchasing', received = 'received', @@ -39,6 +41,8 @@ export default { depositing: 'depositing', dropped: 'dropped', failed: 'failed', + minted:'minted', + minting: 'minting', purchased: 'purchased', purchasing: 'purchasing', received: 'received', diff --git a/src/entities/transactions/transactionType.ts b/src/entities/transactions/transactionType.ts index 62ad3f83fea..b64f2275ad5 100644 --- a/src/entities/transactions/transactionType.ts +++ b/src/entities/transactions/transactionType.ts @@ -7,6 +7,7 @@ export enum TransactionType { deposit = 'deposit', dropped = 'dropped', execution = 'execution', + mint = 'mint', purchase = 'purchase', // Rainbow-specific type receive = 'receive', repay = 'repay', @@ -25,6 +26,7 @@ export default { deposit: 'deposit', dropped: 'dropped', execution: 'execution', + mint: 'mint', purchase: 'purchase', receive: 'receive', repay: 'repay', diff --git a/src/graphql/config.js b/src/graphql/config.js index 69bbea91490..dce581c0510 100644 --- a/src/graphql/config.js +++ b/src/graphql/config.js @@ -17,9 +17,8 @@ exports.config = { document: './queries/arc.graphql', schema: { method: 'GET', - url: 'https://arc-graphql.rainbow.me/graphql', + url: 'https://arc-graphql.rainbowdotme.workers.dev/graphql', headers: { - 'x-api-key': 'ARC_GRAPHQL_API_KEY', }, }, }, diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 16f276838c0..63c4dd57a63 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -9,5 +9,5 @@ export const ensClient = getEnsSdk(getFetchRequester(config.ens)); export const metadataClient = getMetadataSdk( getFetchRequester(config.metadata) ); -export const arcClient = getArcSdk(getFetchRequester(config.arc)); +export const arcClient = getArcDevSdk(getFetchRequester(config.arcDev)); export const arcDevClient = getArcDevSdk(getFetchRequester(config.arcDev)); diff --git a/src/graphql/queries/arc.graphql b/src/graphql/queries/arc.graphql index 4dcfd7d2dfa..4109dafe4f0 100644 --- a/src/graphql/queries/arc.graphql +++ b/src/graphql/queries/arc.graphql @@ -100,3 +100,43 @@ query claimPoapBySecretWord($walletAddress: String!, $secretWord: String!) { error } } + + +query getSingleCollection($walletAddress: String!, $contractAddress: String!, $chain: Int!) { + getSingleCollection( + walletAddress: $walletAddress + contractAddress: $contractAddress + chain: $chain + ) { + collection { + # URL on mint fun + externalURL + contract + # {chain id}:{address} (ex. 1:0xabc...) - used as ID + contractAddress + chainId + deployer + name + # Collection image of project + imageURL + mintsLastHour + addressesLastHour + # ISO8601 date + lastEvent + # ISO8601 date + firstEvent + totalMints + # max supply isn't always known, so can be undefined + maxSupply + + + + mintStatus { + # the collection endpoint will always return mintable collections + isMintable + # BigNumber string of amount in ETH + price + } + } + } +} diff --git a/src/handlers/transactions.ts b/src/handlers/transactions.ts index 95e46cc58df..b5e427ad99b 100644 --- a/src/handlers/transactions.ts +++ b/src/handlers/transactions.ts @@ -114,6 +114,8 @@ const getConfirmedState = (type?: TransactionType): TransactionStatus => { return TransactionStatus.approved; case TransactionTypes.sell: return TransactionStatus.sold; + case TransactionTypes.mint: + return TransactionStatus.minted; case TransactionTypes.deposit: return TransactionStatus.deposited; case TransactionTypes.withdraw: diff --git a/src/helpers/transactions.ts b/src/helpers/transactions.ts index 1c46098a656..8140a9844e8 100644 --- a/src/helpers/transactions.ts +++ b/src/helpers/transactions.ts @@ -96,6 +96,8 @@ export const getConfirmedState = ( return TransactionStatus.purchased; case TransactionTypes.sell: return TransactionStatus.sold; + case TransactionTypes.mint: + return TransactionStatus.minted; default: return TransactionStatus.sent; } diff --git a/src/navigation/Routes.ios.js b/src/navigation/Routes.ios.js index 6cf8f03c3a5..6eb4ab224c1 100644 --- a/src/navigation/Routes.ios.js +++ b/src/navigation/Routes.ios.js @@ -95,6 +95,7 @@ import PoapSheet from '@/screens/mints/PoapSheet'; import { PositionSheet } from '@/screens/positions/PositionSheet'; import { NFTOffersSheet } from '@/screens/NFTOffersSheet'; import { NFTSingleOfferSheet } from '@/screens/NFTSingleOfferSheet'; +import MintSheet from '@/screens/mints/MintSheet'; const Stack = createStackNavigator(); const NativeStack = createNativeStackNavigator(); @@ -208,6 +209,11 @@ function NativeStackNavigator() { component={PoapSheet} name={Routes.POAP_SHEET} {...expandedAssetSheetConfigWithLimit} + /> + { - - { - - + + {hardwareTXError && ( diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx new file mode 100644 index 00000000000..90ab251227e --- /dev/null +++ b/src/screens/mints/MintSheet.tsx @@ -0,0 +1,796 @@ +import { BlurView } from '@react-native-community/blur'; + +import React, { + ReactNode, + ReducerState, + useCallback, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react'; +import { ColorValue, Linking, View } from 'react-native'; +import { useSharedValue } from 'react-native-reanimated'; + +import useWallets from '../../hooks/useWallets'; +import { GasSpeedButton } from '@/components/gas'; +import { Execute, getClient } from '@reservoir0x/reservoir-sdk'; +import { privateKeyToAccount } from 'viem/accounts'; +import { createWalletClient, http } from 'viem'; +import { dataAddNewTransaction } from '@/redux/data'; +import { HoldToAuthorizeButton } from '@/components/buttons'; + + + +import Routes from '@/navigation/routesNames'; + + +import ImgixImage from '../../components/images/ImgixImage'; +import { + SheetActionButton, + SheetActionButtonRow, + SlackSheet, +} from '../../components/sheet'; +import { CardSize } from '../../components/unique-token/CardSize'; +import { + Bleed, + Box, + ColorModeProvider, + Column, + Columns, + DebugLayout, + Heading, + HeadingProps, + Inline, + Inset, + Row, + Rows, + Separator, + Space, + Stack, + Text, +} from '@/design-system'; +import { useAccountProfile, useDimensions, useENSAvatar, useGas, usePersistentAspectRatio } from '@/hooks'; +import { useNavigation } from '@/navigation'; +import styled from '@/styled-thing'; +import { position } from '@/styles'; +import { useTheme } from '@/theme'; +import { CoinIcon, abbreviations, ethereumUtils, gasUtils, watchingAlert } from '@/utils'; +import { usePersistentDominantColorFromImage } from '@/hooks/usePersistentDominantColorFromImage'; +import { maybeSignUri } from '@/handlers/imgix'; +import { ButtonPressAnimation } from '@/components/animations'; +import { useFocusEffect, useRoute } from '@react-navigation/native'; +import { MintCollection } from '@/graphql/__generated__/arcDev'; +import { format } from 'date-fns'; +import { arcClient, arcDevClient } from '@/graphql'; +import Spinner from '@/components/Spinner'; +import { delay } from '@/utils/delay'; +import { useLegacyNFTs } from '@/resources/nfts'; +import { ParsedAddressAsset, TransactionStatus, TransactionType, UniqueAsset } from '@/entities'; +import { IS_DEV, IS_IOS } from '@/env'; +import * as i18n from '@/languages'; +import { PoapMintError } from '@/utils/poaps'; +import { analyticsV2 } from '@/analytics'; +import { event } from '@/analytics/event'; +import { ETH_ADDRESS, ETH_SYMBOL, ethUnits } from '@/references'; +import { RainbowNetworks, getNetworkObj } from '@/networks'; +import { Network } from '@/networks/types'; +import { UniqueTokenImage } from '@/components/unique-token'; +import { fetchReverseRecord } from '@/handlers/ens'; +import { ContactAvatar } from '@/components/contacts'; +import ImageAvatar from '@/components/contacts/ImageAvatar'; +import { addressHashedColorIndex } from '@/utils/profileUtils'; +import { loadPrivateKey } from '@/model/wallet'; +import { current } from 'immer'; +import { ChainBadge } from '@/components/coin-icon'; +import { convertRawAmountToBalance, handleSignificantDecimals, multiply } from '@/helpers/utilities'; +import { RainbowError, logger } from '@/logger'; +import { useDispatch } from 'react-redux'; +import { DOGConfetti } from '@/components/floating-emojis/DOGConfetti'; +import { QuantityButton } from './components/QuantityButton'; +import { estimateGas, estimateGasLimit, getProviderForNetwork } from '@/handlers/web3'; + + +const NFT_IMAGE_HEIGHT = 250; + +const BackgroundBlur = styled(BlurView).attrs({ + blurAmount: 100, + blurType: 'light', +})({ + ...position.coverAsObject, +}); + +const BackgroundImage = styled(View)({ + ...position.coverAsObject, +}); + +interface BlurWrapperProps { + height: number; + width: number; +} + +const BlurWrapper = styled(View).attrs({ + shouldRasterizeIOS: true, +})({ + // @ts-expect-error missing theme types + backgroundColor: ({ theme: { colors } }) => colors.trueBlack, + height: ({ height }: BlurWrapperProps) => height, + left: 0, + overflow: 'hidden', + position: 'absolute', + width: ({ width }: BlurWrapperProps) => width, + ...(android ? { borderTopLeftRadius: 30, borderTopRightRadius: 30 } : {}), +}); + +interface MintSheetProps { + collection: MintCollection; +} + +type PoapClaimStatus = 'none' | 'claiming' | 'claimed' | 'error'; + + + function MintInfoRow({ + symbol, + label, + value, + }: { + symbol: string; + label: string; + value: React.ReactNode; + }) { + return ( + + + + + + + {symbol} + + + + + {label} + + + + {value} + + + ); + } + + + + + +const MintSheet = () => { + const params = useRoute(); + const mintCollection = (params.params as MintSheetProps)?.collection; + const { accountAddress } = useAccountProfile(); + const { height: deviceHeight, width: deviceWidth } = useDimensions(); + const { navigate, goBack } = useNavigation(); + const dispatch = useDispatch(); + const { colors, isDarkMode, lightScheme } = useTheme(); + const { isReadOnlyWallet } = useWallets(); + const currentNetwork = RainbowNetworks.find(({id})=> id === mintCollection.chainId)?.value || Network.mainnet +getNetworkObj(currentNetwork).nativeCurrency + +const didErrorRef = useRef(false); +const didCompleteRef = useRef(false); +const txsRef = useRef([]); + +const [nativeAsset, setNativeAsset] = useState(); +const [quantity, setQuantity] = useReducer((quantity: number, increment: number) => { + if (quantity === 1 && increment === -1) { + return quantity + } + + return quantity + increment +}, 1) + +useEffect(()=>{ + const getNativeAsset = async () =>{ + const asset = await ethereumUtils.getNativeAssetForNetwork( + currentNetwork, + accountAddress + ); + if (asset){ + setNativeAsset(asset) + } + } + + getNativeAsset(); + +},[accountAddress, currentNetwork]) + + const { + gasLimit, + updateTxFee, + startPollingGasFees, + stopPollingGasFees, + isSufficientGas, + selectedGasFee, + isValidGas, + updateDefaultGasLimit, + updateGasFeeOption + } = useGas({nativeAsset}); + + const insufficientEth = isSufficientGas === false && isValidGas; + + const { + data: { nfts }, + } = useLegacyNFTs({ + address: accountAddress, + }); + const imageUrl = maybeSignUri(mintCollection.imageURL); + const {result: aspectRatio} = usePersistentAspectRatio(imageUrl || '') + + + + + + const [errorCode, setErrorCode] = useState( + undefined + ); + + const [nft, setNft] = useState(null); + + + const getFormattedDate = (date: string) => { + return format(new Date(date), 'MMMM dd, yyyy'); + }; + + const isTrending = mintCollection.mintsLastHour > 100; + + const imageColor = + usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; + + const sheetRef = useRef(); + const yPosition = useSharedValue(0); + + const actionOnPress = useCallback(async () => { + logger.info('Minting NFT', {name: mintCollection.name}) + + const privateKey = await loadPrivateKey(accountAddress, false); + // @ts-ignore + const account = privateKeyToAccount(privateKey); + const networkObj = getNetworkObj(currentNetwork); + const signer = createWalletClient({ + account, + chain: networkObj, + transport: http(networkObj.rpc), + }); + + + getClient()?.actions.buyToken({ + items: [{ fillType: "mint", collection: mintCollection.contract }], + wallet: signer!, + chainId: networkObj.id, + onProgress: (steps: Execute['steps']) => { + steps.forEach(step => { + if (step.error && !didErrorRef.current) { + didErrorRef.current = true; + logger.error( + new RainbowError( + `Error minting NFT: ${step.error}` + ) + ); + // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { + // status: 'failed', + // ...analyticsEventObject, + // }); + return; + console.log(step) + } + step.items?.forEach(item => { + if ( + item.txHash && + !txsRef.current.includes(item.txHash) && + item.status === 'incomplete' + ) { + + + const tx = { + to: item.data?.to, + from: item.data?.from, + hash: item.txHash, + network: currentNetwork, + amount: mintPriceDisplay.amount, + asset: { + address: ETH_ADDRESS, + symbol: ETH_SYMBOL, + }, + nft: { + predominantColor: imageColor, + collection: { + image: imageUrl + }, + lowResUrl: imageUrl, + name: mintCollection.name + }, + type: TransactionType.mint, + status: TransactionStatus.minting, + }; + + + if (tx) { + console.log({txNetwork: tx.network}) + txsRef.current.push(tx.hash); + // @ts-ignore TODO: fix when we overhaul tx list, types are not good + dispatch(dataAddNewTransaction(tx)); + navigate(Routes.PROFILE_SCREEN); + } + } + }) + + }) + } + }) + + // if (claimStatus === 'claimed') { + // if (nft) { + // navigate(Routes.EXPANDED_ASSET_SHEET, { + // asset: nft, + // backgroundOpacity: 1, + // cornerRadius: 'device', + // external: false, + // springDamping: 1, + // topOffset: 0, + // transitionDuration: 0.25, + // type: 'unique_token', + // }); + // } + // } else { + // if (poapMintType === 'secretWord') { + // await claimPoapBySecret(); + // } else { + // await claimPoapByQrHash(); + // } + // } + }, []); + + useEffect(() => { + // const nft = nfts.find( + // item => item.image_original_url === poapEvent.imageUrl + // ); + // if (nft) { + // setClaimStatus('claimed'); + // setNft(nft); + // } + }, [imageUrl, nfts]); + + const getErrorMessage = () => { + if (errorCode === 'LIMIT_EXCEEDED') { + return i18n.t(i18n.l.poaps.error_messages.limit_exceeded); + } else if (errorCode === 'EVENT_EXPIRED') { + return i18n.t(i18n.l.poaps.error_messages.event_expired); + } + return i18n.t(i18n.l.poaps.error_messages.event_expired); + }; + + useFocusEffect(() => { + //analytics go here + }); + + + useEffect(()=>{ + const fetchENSName = async (address: string) =>{ + const ensName = await fetchReverseRecord(address); + console.log({ensName}) + setENSName(ensName); + + } +if (mintCollection.deployer){ + fetchENSName(mintCollection.deployer) +} + },[mintCollection.deployer]) + + // estimate gas + useEffect(() => { + startPollingGasFees(currentNetwork); + + return () => { + stopPollingGasFees(); + }; + }, [currentNetwork,startPollingGasFees, stopPollingGasFees, ]); + + useEffect(()=>{ + + const estimateMintGas = async () => { + console.log('updating tx fee') + + updateTxFee(ethUnits.basic_swap, null, ethUnits.default_l1_gas_fee_optimism_swap); + + const networkObj = getNetworkObj(currentNetwork); + const provider = await getProviderForNetwork(currentNetwork) + const signer = createWalletClient({ + account: accountAddress, + chain: networkObj, + transport: http(networkObj.rpc), + }); + + getClient()?.actions.buyToken({ + items: [{ fillType: "mint", collection: mintCollection.contract, quantity }], + wallet: signer!, + chainId: networkObj.id, + precheck: true, + onProgress: async (steps: Execute['steps']) => { + steps.forEach(step => { + if (step.error && !didErrorRef.current) { + didErrorRef.current = true; + logger.error( + new RainbowError( + `Error minting NFT: ${step.error}` + ) + ); + // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { + // status: 'failed', + // ...analyticsEventObject, + // }); + return; + } + step.items?.forEach(async item => { + console.log('STEP') + + + + const tx = { + to: item.data?.to, + from: item.data?.from, + data: item.data?.data, + value: multiply(mintCollection.mintStatus?.price || '0', quantity), + + }; + const gas = await estimateGas(tx, provider) +if (gas){ + console.log({gas}) + updateTxFee(gas, null); +} + + + }) + + }) + } + }) + } + + estimateMintGas(); + + },[accountAddress, currentNetwork, mintCollection.contract, mintCollection.mintStatus?.price, quantity, updateTxFee]) + + const [ensName, setENSName] = useState(''); + + + const { data: ensAvatar } = useENSAvatar(ensName, { + enabled: Boolean(ensName), + }); + + + const deployerDisplay = abbreviations.address(mintCollection.deployer || '', 4, 6) + const contractAddressDisplay = `${abbreviations.address(mintCollection.contract || '', 4, 6)} 􀄯` + const mintPriceDisplay = convertRawAmountToBalance( + multiply(mintCollection.mintStatus?.price || '0', quantity), + { + decimals: 18, + symbol: 'ETH' + } + // abbreviate if amount is >= 10,000 + ); + return ( + <> + {ios && ( + + + + + + + )} + {/* @ts-expect-error JavaScript component */} + + + + + + + + + + + + + + + + + + + + + + + + {mintCollection.name} + + + + {`By `} + + + { ensAvatar?.imageUrl ? + : + + } + + {` ${ensName || deployerDisplay || 'unknown'}`} + + + + + + + + + + + + + + + + + + + + {'Mint Price'} + + + + {mintPriceDisplay.amount === '0' ? 'Free' : mintPriceDisplay.display} + + + + + + + + setQuantity(1)} + minusAction={() => setQuantity(-1)} + buttonColor={imageColor} /> + + + + + + + + + {/* @ts-ignore */} + + + + + + + + {/* @ts-ignore */} + + + + + + + + + + + { + /* + nPress={()=> ethereumUtils.openAddressInBlockExplorer(mintCollection?.contract, currentNetwork)} value={contractAddressDisplay} color={imageColor} + + */ + } + + + + + + + + + + + + + + + + + + + + {true && + {`${mintCollection.totalMints} NFTs`} + }/>} + {mintCollection?.firstEvent && + {getFormattedDate(mintCollection?.firstEvent)} + } />} + + {mintCollection?.lastEvent && + {getFormattedDate(mintCollection?.lastEvent)} + } />} + + {mintCollection?.maxSupply && + {`${mintCollection.maxSupply} NFTs`} + } />} + + + {mintCollection?.contract && ethereumUtils.openAddressInBlockExplorer(mintCollection.contract, currentNetwork)}> + + {contractAddressDisplay} + + + + + }/>} + + + + + + + + {currentNetwork === Network.mainnet ? ( + + ) : ( + + )} + + {`${getNetworkObj(currentNetwork).name}`} + + + + } /> + + + + + + + + + + + + + + ); +}; + +export default MintSheet; diff --git a/src/screens/mints/components/QuantityButton.tsx b/src/screens/mints/components/QuantityButton.tsx new file mode 100644 index 00000000000..3524e8460f7 --- /dev/null +++ b/src/screens/mints/components/QuantityButton.tsx @@ -0,0 +1,163 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import ReactNativeHapticFeedback from 'react-native-haptic-feedback'; +import { delay } from '@/utils/delay'; +import { usePrevious } from '@/hooks'; +import styled from '@/styled-thing'; +import { ButtonPressAnimation } from '@/components/animations'; +import Row from '@/components/layout/Row'; +import { Box, Inline, Text } from '@/design-system'; +import { colors } from '@/styles'; + +const PLUS_ACTION_TYPE = 'plus'; +const MINUS_ACTION_TYPE = 'minus'; +const LONG_PRESS_DELAY_THRESHOLD = 69; +const MIN_LONG_PRESS_DELAY_THRESHOLD = 200; + +const Wrapper = styled(Row)({}); + +const StepButtonWrapper = styled(ButtonPressAnimation).attrs(() => ({ + paddingHorizontal: 7, + scaleTo: 0.75, +}))({}); + + + +type StepButtonProps = { + type: 'plus' | 'minus', + onLongPress: () => void, + onLongPressEnded: () => void, + onPress: () => void, + shouldLongPressHoldPress: boolean, + buttonColor: string +} +const StepButton = ({ + type, + onLongPress, + onLongPressEnded, + onPress, + shouldLongPressHoldPress, + buttonColor, +}: StepButtonProps) => { + return ( + + + + {type === 'plus' ? '􀅼' : '􀅽'} + + + + + ); +}; + + +type StepButtonInputProps = { + value: number, + plusAction: ()=> void, + minusAction: ()=> void, + buttonColor: string +} +export function QuantityButton({ + value, + plusAction, + minusAction, + buttonColor, +}: StepButtonInputProps) { + const longPressHandle = useRef(null); + const [trigger, setTrigger] = useState(false); + const [actionType, setActionType] = useState<'plus' | 'minus' | null>(null); + const prevTrigger = usePrevious(trigger); + + const onMinusPress = useCallback(() => { + longPressHandle.current = false; + minusAction(); + }, [minusAction]); + + const onPlusPress = useCallback(() => { + longPressHandle.current = false; + plusAction(); + }, [plusAction]); + + const onLongPressEnded = useCallback(() => { + longPressHandle.current = false; + setActionType(null); + }, [longPressHandle]); + + const onLongPressLoop = useCallback(async () => { + setTrigger(true); + setTrigger(false); + await delay(LONG_PRESS_DELAY_THRESHOLD); + longPressHandle.current && onLongPressLoop(); + }, []); + + const onLongPress = useCallback(async () => { + longPressHandle.current = true; + onLongPressLoop(); + }, [onLongPressLoop]); + + const onPlusLongPress = useCallback(() => { + setActionType(PLUS_ACTION_TYPE); + onLongPress(); + }, [onLongPress]); + + const onMinusLongPress = useCallback(() => { + setActionType(MINUS_ACTION_TYPE); + onLongPress(); + }, [onLongPress]); + + useEffect(() => { + if (!prevTrigger && trigger) { + if (actionType === PLUS_ACTION_TYPE) { + plusAction(); + if (!android) { + ReactNativeHapticFeedback.trigger('selection'); + } + } else if (actionType === MINUS_ACTION_TYPE) { + minusAction(); + if (!android) { + ReactNativeHapticFeedback.trigger('selection'); + } + } + } + }, [trigger, prevTrigger, actionType, plusAction, minusAction]); + + return ( + + + + + + + {value} + + + + + + ); +} From 6eb4f5253e970655e7761b9b3cb0f772df964a36 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Fri, 25 Aug 2023 16:22:55 -0400 Subject: [PATCH 02/44] inti --- package.json | 2 +- .../coin-row/FastTransactionCoinRow.tsx | 11 + .../coin-row/FastTransactionStatusBadge.tsx | 9 + src/languages/_english.json | 11 + src/languages/_french.json | 10 + src/resources/nftOffers/utils.ts | 29 +- .../discover/components/DiscoverSearch.js | 33 + src/screens/mints/MintSheet.tsx | 830 +++++++++--------- yarn.lock | 8 +- 9 files changed, 531 insertions(+), 412 deletions(-) diff --git a/package.json b/package.json index a953aad7a09..d32b7ea0b4b 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "@react-navigation/material-top-tabs": "6.6.2", "@react-navigation/native": "6.1.6", "@react-navigation/stack": "6.3.16", - "@reservoir0x/reservoir-sdk": "1.1.24", + "@reservoir0x/reservoir-sdk": "1.2.1", "@segment/analytics-react-native": "2.15.0", "@segment/sovran-react-native": "1.0.4", "@sentry/react-native": "3.4.1", diff --git a/src/components/coin-row/FastTransactionCoinRow.tsx b/src/components/coin-row/FastTransactionCoinRow.tsx index 57ee057215d..19511125b42 100644 --- a/src/components/coin-row/FastTransactionCoinRow.tsx +++ b/src/components/coin-row/FastTransactionCoinRow.tsx @@ -10,6 +10,9 @@ import { useNavigation } from '@/navigation'; import Routes from '@rainbow-me/routes'; import { ImgixImage } from '../images'; import { CardSize } from '../unique-token/CardSize'; +import { ChainBadge } from '../coin-icon'; +import { Network } from '@/networks/types'; +import { ethereumUtils } from '@/utils'; const BottomRow = React.memo(function BottomRow({ description, @@ -139,6 +142,14 @@ export default React.memo(function TransactionCoinRow({ }} /> + {item.network !== Network.mainnet && ( + + )} ) : ( { + const checkAndHandleMint = async seachQueryForMint => { + if (seachQueryForMint.includes('mint.fun')) { + const mintdotfunURL = seachQueryForMint.split('https://mint.fun/'); + console.log(seachQueryForMint); + console.log(mintdotfunURL); + const query = mintdotfunURL[1]; + let network = query.split('/')[0]; + if (network === 'ethereum') { + network = 'mainnet'; + } + const contractAddress = query.split('/')[1]; + const chainID = getNetworkObj(network).id; + console.log({ chainID, contractAddress }); + const res = await arcDevClient.getSingleCollection({ + walletAddress: accountAddress, + contractAddress, + chain: chainID, + }); + if (res) { + navigate(Routes.MINT_SHEET, { + collection: res.getSingleCollection?.collection, + }); + } + } + }; + checkAndHandleMint(searchQuery); + }, [accountAddress, navigate, searchQuery]); + const handlePress = useCallback( item => { if (item.ens) { diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 90ab251227e..f867e272712 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -17,15 +17,12 @@ import useWallets from '../../hooks/useWallets'; import { GasSpeedButton } from '@/components/gas'; import { Execute, getClient } from '@reservoir0x/reservoir-sdk'; import { privateKeyToAccount } from 'viem/accounts'; -import { createWalletClient, http } from 'viem'; +import { createWalletClient, http } from 'viem'; import { dataAddNewTransaction } from '@/redux/data'; import { HoldToAuthorizeButton } from '@/components/buttons'; - - import Routes from '@/navigation/routesNames'; - import ImgixImage from '../../components/images/ImgixImage'; import { SheetActionButton, @@ -51,12 +48,24 @@ import { Stack, Text, } from '@/design-system'; -import { useAccountProfile, useDimensions, useENSAvatar, useGas, usePersistentAspectRatio } from '@/hooks'; +import { + useAccountProfile, + useDimensions, + useENSAvatar, + useGas, + usePersistentAspectRatio, +} from '@/hooks'; import { useNavigation } from '@/navigation'; import styled from '@/styled-thing'; import { position } from '@/styles'; import { useTheme } from '@/theme'; -import { CoinIcon, abbreviations, ethereumUtils, gasUtils, watchingAlert } from '@/utils'; +import { + CoinIcon, + abbreviations, + ethereumUtils, + gasUtils, + watchingAlert, +} from '@/utils'; import { usePersistentDominantColorFromImage } from '@/hooks/usePersistentDominantColorFromImage'; import { maybeSignUri } from '@/handlers/imgix'; import { ButtonPressAnimation } from '@/components/animations'; @@ -67,7 +76,12 @@ import { arcClient, arcDevClient } from '@/graphql'; import Spinner from '@/components/Spinner'; import { delay } from '@/utils/delay'; import { useLegacyNFTs } from '@/resources/nfts'; -import { ParsedAddressAsset, TransactionStatus, TransactionType, UniqueAsset } from '@/entities'; +import { + ParsedAddressAsset, + TransactionStatus, + TransactionType, + UniqueAsset, +} from '@/entities'; import { IS_DEV, IS_IOS } from '@/env'; import * as i18n from '@/languages'; import { PoapMintError } from '@/utils/poaps'; @@ -84,13 +98,20 @@ import { addressHashedColorIndex } from '@/utils/profileUtils'; import { loadPrivateKey } from '@/model/wallet'; import { current } from 'immer'; import { ChainBadge } from '@/components/coin-icon'; -import { convertRawAmountToBalance, handleSignificantDecimals, multiply } from '@/helpers/utilities'; +import { + convertRawAmountToBalance, + handleSignificantDecimals, + multiply, +} from '@/helpers/utilities'; import { RainbowError, logger } from '@/logger'; import { useDispatch } from 'react-redux'; import { DOGConfetti } from '@/components/floating-emojis/DOGConfetti'; import { QuantityButton } from './components/QuantityButton'; -import { estimateGas, estimateGasLimit, getProviderForNetwork } from '@/handlers/web3'; - +import { + estimateGas, + estimateGasLimit, + getProviderForNetwork, +} from '@/handlers/web3'; const NFT_IMAGE_HEIGHT = 250; @@ -129,41 +150,36 @@ interface MintSheetProps { type PoapClaimStatus = 'none' | 'claiming' | 'claimed' | 'error'; +function MintInfoRow({ + symbol, + label, + value, +}: { + symbol: string; + label: string; + value: React.ReactNode; +}) { + return ( + + + + + + + {symbol} + + - function MintInfoRow({ - symbol, - label, - value, - }: { - symbol: string; - label: string; - value: React.ReactNode; - }) { - return ( - - - - - - - {symbol} - - - - - {label} - - - - {value} - - - ); - } - - - - + + {label} + + + + {value} + + + ); +} const MintSheet = () => { const params = useRoute(); @@ -174,36 +190,42 @@ const MintSheet = () => { const dispatch = useDispatch(); const { colors, isDarkMode, lightScheme } = useTheme(); const { isReadOnlyWallet } = useWallets(); - const currentNetwork = RainbowNetworks.find(({id})=> id === mintCollection.chainId)?.value || Network.mainnet -getNetworkObj(currentNetwork).nativeCurrency - -const didErrorRef = useRef(false); -const didCompleteRef = useRef(false); -const txsRef = useRef([]); - -const [nativeAsset, setNativeAsset] = useState(); -const [quantity, setQuantity] = useReducer((quantity: number, increment: number) => { - if (quantity === 1 && increment === -1) { - return quantity - } - - return quantity + increment -}, 1) - -useEffect(()=>{ - const getNativeAsset = async () =>{ - const asset = await ethereumUtils.getNativeAssetForNetwork( - currentNetwork, - accountAddress - ); - if (asset){ - setNativeAsset(asset) - } - } + const currentNetwork = + RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || + Network.mainnet; + getNetworkObj(currentNetwork).nativeCurrency; + + const didErrorRef = useRef(false); + const didCompleteRef = useRef(false); + const txsRef = useRef([]); + + const [nativeAsset, setNativeAsset] = useState< + ParsedAddressAsset | undefined + >(); + const [quantity, setQuantity] = useReducer( + (quantity: number, increment: number) => { + if (quantity === 1 && increment === -1) { + return quantity; + } + + return quantity + increment; + }, + 1 + ); - getNativeAsset(); + useEffect(() => { + const getNativeAsset = async () => { + const asset = await ethereumUtils.getNativeAssetForNetwork( + currentNetwork, + accountAddress + ); + if (asset) { + setNativeAsset(asset); + } + }; -},[accountAddress, currentNetwork]) + getNativeAsset(); + }, [accountAddress, currentNetwork]); const { gasLimit, @@ -214,8 +236,8 @@ useEffect(()=>{ selectedGasFee, isValidGas, updateDefaultGasLimit, - updateGasFeeOption - } = useGas({nativeAsset}); + updateGasFeeOption, + } = useGas({ nativeAsset }); const insufficientEth = isSufficientGas === false && isValidGas; @@ -225,11 +247,7 @@ useEffect(()=>{ address: accountAddress, }); const imageUrl = maybeSignUri(mintCollection.imageURL); - const {result: aspectRatio} = usePersistentAspectRatio(imageUrl || '') - - - - + const { result: aspectRatio } = usePersistentAspectRatio(imageUrl || ''); const [errorCode, setErrorCode] = useState( undefined @@ -237,7 +255,6 @@ useEffect(()=>{ const [nft, setNft] = useState(null); - const getFormattedDate = (date: string) => { return format(new Date(date), 'MMMM dd, yyyy'); }; @@ -251,7 +268,7 @@ useEffect(()=>{ const yPosition = useSharedValue(0); const actionOnPress = useCallback(async () => { - logger.info('Minting NFT', {name: mintCollection.name}) + logger.info('Minting NFT', { name: mintCollection.name }); const privateKey = await loadPrivateKey(accountAddress, false); // @ts-ignore @@ -263,26 +280,23 @@ useEffect(()=>{ transport: http(networkObj.rpc), }); - getClient()?.actions.buyToken({ - items: [{ fillType: "mint", collection: mintCollection.contract }], + items: [ + { fillType: 'mint', collection: mintCollection.contract, quantity }, + ], wallet: signer!, chainId: networkObj.id, onProgress: (steps: Execute['steps']) => { steps.forEach(step => { if (step.error && !didErrorRef.current) { didErrorRef.current = true; - logger.error( - new RainbowError( - `Error minting NFT: ${step.error}` - ) - ); + logger.error(new RainbowError(`Error minting NFT: ${step.error}`)); // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { // status: 'failed', // ...analyticsEventObject, // }); return; - console.log(step) + console.log(step); } step.items?.forEach(item => { if ( @@ -290,45 +304,41 @@ useEffect(()=>{ !txsRef.current.includes(item.txHash) && item.status === 'incomplete' ) { - - - const tx = { - to: item.data?.to, - from: item.data?.from, - hash: item.txHash, - network: currentNetwork, - amount: mintPriceDisplay.amount, - asset: { - address: ETH_ADDRESS, - symbol: ETH_SYMBOL, - }, - nft: { - predominantColor: imageColor, - collection: { - image: imageUrl + const tx = { + to: item.data?.to, + from: item.data?.from, + hash: item.txHash, + network: currentNetwork, + amount: mintPriceDisplay.amount, + asset: { + address: ETH_ADDRESS, + symbol: ETH_SYMBOL, + }, + nft: { + predominantColor: imageColor, + collection: { + image: imageUrl, + }, + lowResUrl: imageUrl, + name: mintCollection.name, }, - lowResUrl: imageUrl, - name: mintCollection.name - }, - type: TransactionType.mint, - status: TransactionStatus.minting, - }; - - - if (tx) { - console.log({txNetwork: tx.network}) - txsRef.current.push(tx.hash); - // @ts-ignore TODO: fix when we overhaul tx list, types are not good - dispatch(dataAddNewTransaction(tx)); - navigate(Routes.PROFILE_SCREEN); + type: TransactionType.mint, + status: TransactionStatus.minting, + }; + + if (tx) { + console.log({ txNetwork: tx.network }); + txsRef.current.push(tx.hash); + // @ts-ignore TODO: fix when we overhaul tx list, types are not good + dispatch(dataAddNewTransaction(tx)); + navigate(Routes.PROFILE_SCREEN); + } } - } - }) + }); + }); + }, + }); - }) - } - }) - // if (claimStatus === 'claimed') { // if (nft) { // navigate(Routes.EXPANDED_ASSET_SHEET, { @@ -371,21 +381,18 @@ useEffect(()=>{ }; useFocusEffect(() => { - //analytics go here + //analytics go here }); - - useEffect(()=>{ - const fetchENSName = async (address: string) =>{ + useEffect(() => { + const fetchENSName = async (address: string) => { const ensName = await fetchReverseRecord(address); - console.log({ensName}) setENSName(ensName); - + }; + if (mintCollection.deployer) { + fetchENSName(mintCollection.deployer); } -if (mintCollection.deployer){ - fetchENSName(mintCollection.deployer) -} - },[mintCollection.deployer]) + }, [mintCollection.deployer]); // estimate gas useEffect(() => { @@ -394,88 +401,102 @@ if (mintCollection.deployer){ return () => { stopPollingGasFees(); }; - }, [currentNetwork,startPollingGasFees, stopPollingGasFees, ]); - - useEffect(()=>{ + }, [currentNetwork, startPollingGasFees, stopPollingGasFees]); + useEffect(() => { const estimateMintGas = async () => { - console.log('updating tx fee') - - updateTxFee(ethUnits.basic_swap, null, ethUnits.default_l1_gas_fee_optimism_swap); - - const networkObj = getNetworkObj(currentNetwork); - const provider = await getProviderForNetwork(currentNetwork) - const signer = createWalletClient({ - account: accountAddress, - chain: networkObj, - transport: http(networkObj.rpc), - }); - - getClient()?.actions.buyToken({ - items: [{ fillType: "mint", collection: mintCollection.contract, quantity }], - wallet: signer!, - chainId: networkObj.id, - precheck: true, - onProgress: async (steps: Execute['steps']) => { - steps.forEach(step => { - if (step.error && !didErrorRef.current) { - didErrorRef.current = true; - logger.error( - new RainbowError( - `Error minting NFT: ${step.error}` - ) - ); - // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { - // status: 'failed', - // ...analyticsEventObject, - // }); - return; - } - step.items?.forEach(async item => { - console.log('STEP') - - - - const tx = { - to: item.data?.to, - from: item.data?.from, - data: item.data?.data, - value: multiply(mintCollection.mintStatus?.price || '0', quantity), - - }; - const gas = await estimateGas(tx, provider) -if (gas){ - console.log({gas}) - updateTxFee(gas, null); -} - - - }) - - }) - } - }) - } + updateTxFee( + ethUnits.basic_swap, + null, + ethUnits.default_l1_gas_fee_optimism_swap + ); - estimateMintGas(); + const networkObj = getNetworkObj(currentNetwork); + const provider = await getProviderForNetwork(currentNetwork); + const signer = createWalletClient({ + account: accountAddress, + chain: networkObj, + transport: http(networkObj.rpc), + }); + + getClient()?.actions.buyToken({ + items: [ + { fillType: 'mint', collection: mintCollection.contract, quantity }, + ], + wallet: signer!, + chainId: networkObj.id, + precheck: true, + onProgress: async (steps: Execute['steps']) => { + steps.forEach(step => { + if (step.error && !didErrorRef.current) { + didErrorRef.current = true; + logger.error( + new RainbowError(`Error minting NFT: ${step.error}`) + ); + // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { + // status: 'failed', + // ...analyticsEventObject, + // }); + return; + } + step.items?.forEach(async item => { + const tx = { + to: item.data?.to, + from: item.data?.from, + data: item.data?.data, + value: multiply( + mintCollection.mintStatus?.price || '0', + quantity + ), + }; + const gas = await estimateGas(tx, provider); + if (gas) { + updateTxFee(gas, null); + } + { + updateTxFee( + ethUnits.basic_swap, + null, + ethUnits.default_l1_gas_fee_optimism_swap + ); + } + }); + }); + }, + }); + }; - },[accountAddress, currentNetwork, mintCollection.contract, mintCollection.mintStatus?.price, quantity, updateTxFee]) + estimateMintGas(); + }, [ + accountAddress, + currentNetwork, + mintCollection.contract, + mintCollection.mintStatus?.price, + quantity, + updateTxFee, + ]); const [ensName, setENSName] = useState(''); - const { data: ensAvatar } = useENSAvatar(ensName, { enabled: Boolean(ensName), }); - - const deployerDisplay = abbreviations.address(mintCollection.deployer || '', 4, 6) - const contractAddressDisplay = `${abbreviations.address(mintCollection.contract || '', 4, 6)} 􀄯` + const deployerDisplay = abbreviations.address( + mintCollection.deployer || '', + 4, + 6 + ); + const contractAddressDisplay = `${abbreviations.address( + mintCollection.contract || '', + 4, + 6 + )} 􀄯`; const mintPriceDisplay = convertRawAmountToBalance( multiply(mintCollection.mintStatus?.price || '0', quantity), { decimals: 18, - symbol: 'ETH' + symbol: 'ETH', } // abbreviate if amount is >= 10,000 ); @@ -504,24 +525,18 @@ if (gas){ height={'100%'} ref={sheetRef} scrollEnabled - testID="poap-mint-sheet" + testID="nft-mint-sheet" yPosition={yPosition} > - - - - - - - + - - - - - - {mintCollection.name} - - - - {`By `} - - - { ensAvatar?.imageUrl ? - : - - } - - {` ${ensName || deployerDisplay || 'unknown'}`} - - - - - - + + + {mintCollection.name} + + + + {`By `} + - + {ensAvatar?.imageUrl ? ( + + ) : ( + + )} + + {` ${ensName || deployerDisplay || 'unknown'}`} + + + + - - - - - - - - - - {'Mint Price'} - + + + - - {mintPriceDisplay.amount === '0' ? 'Free' : mintPriceDisplay.display} - - - - - - - - setQuantity(1)} - minusAction={() => setQuantity(-1)} - buttonColor={imageColor} /> - - - - - - - + + + + + {'Mint Price'} + + + + {mintPriceDisplay.amount === '0' + ? 'Free' + : mintPriceDisplay.display} + + + + + + setQuantity(1)} + minusAction={() => setQuantity(-1)} + buttonColor={imageColor} + /> + + {/* @ts-ignore */} + + {/* @ts-ignore */} + + + - - - - - {/* @ts-ignore */} - - - - - - - - - - - { - /* + {/* nPress={()=> ethereumUtils.openAddressInBlockExplorer(mintCollection?.contract, currentNetwork)} value={contractAddressDisplay} color={imageColor} - */ - } - - - - - + */} - - - - - - + + + + - - - - + - - {true && + {mintCollection?.totalMints && ( + {`${mintCollection.totalMints} NFTs`} - }/>} - {mintCollection?.firstEvent && - {getFormattedDate(mintCollection?.firstEvent)} - } />} - - {mintCollection?.lastEvent && - {getFormattedDate(mintCollection?.lastEvent)} - } />} - - {mintCollection?.maxSupply && + } + /> + )} + {mintCollection?.firstEvent && ( + - {`${mintCollection.maxSupply} NFTs`} - } />} - + {getFormattedDate(mintCollection?.firstEvent)} + + } + /> + )} - {mintCollection?.contract && ethereumUtils.openAddressInBlockExplorer(mintCollection.contract, currentNetwork)}> - - {contractAddressDisplay} + {getFormattedDate(mintCollection?.lastEvent)} + } + /> + )} - - - }/>} - - - - - - - - {currentNetwork === Network.mainnet ? ( - - ) : ( - - )} - - {`${getNetworkObj(currentNetwork).name}`} + {`${mintCollection.maxSupply} NFTs`} + } + /> + )} + + {mintCollection?.contract && ( + + ethereumUtils.openAddressInBlockExplorer( + mintCollection.contract, + currentNetwork + ) + } + > + + {contractAddressDisplay} + + + } + /> + )} + + + + {currentNetwork === Network.mainnet ? ( + + ) : ( + + )} + + {`${getNetworkObj(currentNetwork).name}`} + - - } /> - - - - - - - - + + } + /> + + - diff --git a/yarn.lock b/yarn.lock index 9b90e5fd63d..97ac97a44af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4054,10 +4054,10 @@ color "^4.2.3" warn-once "^0.1.0" -"@reservoir0x/reservoir-sdk@1.1.24": - version "1.1.24" - resolved "https://registry.yarnpkg.com/@reservoir0x/reservoir-sdk/-/reservoir-sdk-1.1.24.tgz#69b8eb52ec11dc087fe715d2e70c22fe66b972a7" - integrity sha512-8CUJi/ut1Ts0HpMazLaAu2u1Rht6pLN/nfNvKhk2+ago4Y7VAxfEbYCLR47r8/urO9Qj2roFUm2gQsS/up1h7g== +"@reservoir0x/reservoir-sdk@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@reservoir0x/reservoir-sdk/-/reservoir-sdk-1.2.1.tgz#8f1a1330c06c658b98e4fd66d0de994c398197ba" + integrity sha512-+FN4mN5m5zEPAW41ZkO23NBc1ANQA8gIJr7bWE44fOm7T0dMtbBKEHuGsLtwbuKUx+IslbjmdhytkXRfFTPMqQ== dependencies: axios "^0.27.2" From 37663f90a4550343ffec3d6cf507dc66ff1b3c1e Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Wed, 13 Sep 2023 14:55:07 -0400 Subject: [PATCH 03/44] new api + routing --- src/graphql/queries/arc.graphql | 70 +++++++++--------- src/resources/reservoir/mints.ts | 41 +++++++++++ src/screens/mints/MintSheet.tsx | 117 ++++++++++++++++--------------- 3 files changed, 141 insertions(+), 87 deletions(-) create mode 100644 src/resources/reservoir/mints.ts diff --git a/src/graphql/queries/arc.graphql b/src/graphql/queries/arc.graphql index 4109dafe4f0..3ae98c2a9ea 100644 --- a/src/graphql/queries/arc.graphql +++ b/src/graphql/queries/arc.graphql @@ -101,41 +101,47 @@ query claimPoapBySecretWord($walletAddress: String!, $secretWord: String!) { } } - -query getSingleCollection($walletAddress: String!, $contractAddress: String!, $chain: Int!) { - getSingleCollection( - walletAddress: $walletAddress - contractAddress: $contractAddress - chain: $chain - ) { +query getReservoirCollection($contractAddress: String!, $chainId: Int!) { + getReserviorCollection(contractAddress: $contractAddress, chainId: $chainId) { collection { - # URL on mint fun - externalURL - contract - # {chain id}:{address} (ex. 1:0xabc...) - used as ID - contractAddress + id chainId - deployer + createdAt name - # Collection image of project - imageURL - mintsLastHour - addressesLastHour - # ISO8601 date - lastEvent - # ISO8601 date - firstEvent - totalMints - # max supply isn't always known, so can be undefined - maxSupply - - - - mintStatus { - # the collection endpoint will always return mintable collections - isMintable - # BigNumber string of amount in ETH - price + image + description + sampleImages + tokenCount + creator + ownerCount + isMintingPublicSale + isMintingPresale + mintStages { + stage + kind + price { + currency { + contract + name + symbol + decimals + } + amount { + raw + decimal + usd + native + } + netAmount { + raw + decimal + usd + native + } + } + startTime + endTime + maxMintsPerWallet } } } diff --git a/src/resources/reservoir/mints.ts b/src/resources/reservoir/mints.ts new file mode 100644 index 00000000000..a66e2ced7ac --- /dev/null +++ b/src/resources/reservoir/mints.ts @@ -0,0 +1,41 @@ +import { EthereumAddress } from '@/entities'; +import { arcDevClient, arcClient } from '@/graphql'; +import { getNetworkObj } from '@/networks'; +import { Navigation } from '@/navigation'; +import { Network } from '@/networks/types'; +import Routes from '@/navigation/routesNames'; +import { IS_DEV } from '@/env'; +import { logger } from '@/logger'; +import { WrappedAlert as Alert } from '@/helpers/alert'; +import * as lang from '@/languages'; + +const client = IS_DEV ? arcDevClient : arcClient; +const showAlert = () => { + Alert.alert( + 'Could not find collection', + 'We are unable to find this collection, double check the address and network or try again later', + [{ text: lang.t(lang.l.button.ok) }], + { cancelable: false } + ); +}; +export const navigateToMintCollection = async ( + contractAddress: EthereumAddress, + network: Network +) => { + try { + const chainId = getNetworkObj(network).id; + const res = await client.getReservoirCollection({ + contractAddress, + chainId, + }); + if (res?.getReserviorCollection?.collection) { + Navigation.handleAction(Routes.MINT_SHEET, { + collection: res.getReserviorCollection?.collection, + }); + } else { + showAlert(); + } + } catch (e) { + showAlert(); + } +}; diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index f867e272712..78478de66ca 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -70,7 +70,7 @@ import { usePersistentDominantColorFromImage } from '@/hooks/usePersistentDomina import { maybeSignUri } from '@/handlers/imgix'; import { ButtonPressAnimation } from '@/components/animations'; import { useFocusEffect, useRoute } from '@react-navigation/native'; -import { MintCollection } from '@/graphql/__generated__/arcDev'; +import { ReservoirCollection } from '@/graphql/__generated__/arcDev'; import { format } from 'date-fns'; import { arcClient, arcDevClient } from '@/graphql'; import Spinner from '@/components/Spinner'; @@ -145,7 +145,8 @@ const BlurWrapper = styled(View).attrs({ }); interface MintSheetProps { - collection: MintCollection; + collection: ReservoirCollection; + chainId: number; } type PoapClaimStatus = 'none' | 'claiming' | 'claimed' | 'error'; @@ -160,7 +161,7 @@ function MintInfoRow({ value: React.ReactNode; }) { return ( - + @@ -183,17 +184,17 @@ function MintInfoRow({ const MintSheet = () => { const params = useRoute(); - const mintCollection = (params.params as MintSheetProps)?.collection; + const { collection: mintCollection } = params.params as MintSheetProps; const { accountAddress } = useAccountProfile(); const { height: deviceHeight, width: deviceWidth } = useDimensions(); const { navigate, goBack } = useNavigation(); const dispatch = useDispatch(); const { colors, isDarkMode, lightScheme } = useTheme(); const { isReadOnlyWallet } = useWallets(); + const currentNetwork = RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || Network.mainnet; - getNetworkObj(currentNetwork).nativeCurrency; const didErrorRef = useRef(false); const didCompleteRef = useRef(false); @@ -246,7 +247,7 @@ const MintSheet = () => { } = useLegacyNFTs({ address: accountAddress, }); - const imageUrl = maybeSignUri(mintCollection.imageURL); + const imageUrl = maybeSignUri(mintCollection.image || ''); const { result: aspectRatio } = usePersistentAspectRatio(imageUrl || ''); const [errorCode, setErrorCode] = useState( @@ -258,8 +259,10 @@ const MintSheet = () => { const getFormattedDate = (date: string) => { return format(new Date(date), 'MMMM dd, yyyy'); }; - - const isTrending = mintCollection.mintsLastHour > 100; + const price = mintCollection.mintStages?.find( + item => item?.stage === 'public-sale' + )?.price?.amount?.raw; + const isMinting = mintCollection.isMintingPublicSale; const imageColor = usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; @@ -281,9 +284,7 @@ const MintSheet = () => { }); getClient()?.actions.buyToken({ - items: [ - { fillType: 'mint', collection: mintCollection.contract, quantity }, - ], + items: [{ fillType: 'mint', collection: mintCollection.id!, quantity }], wallet: signer!, chainId: networkObj.id, onProgress: (steps: Execute['steps']) => { @@ -296,7 +297,6 @@ const MintSheet = () => { // ...analyticsEventObject, // }); return; - console.log(step); } step.items?.forEach(item => { if ( @@ -389,10 +389,10 @@ const MintSheet = () => { const ensName = await fetchReverseRecord(address); setENSName(ensName); }; - if (mintCollection.deployer) { - fetchENSName(mintCollection.deployer); + if (mintCollection.creator) { + fetchENSName(mintCollection.creator); } - }, [mintCollection.deployer]); + }, [mintCollection.creator]); // estimate gas useEffect(() => { @@ -420,9 +420,7 @@ const MintSheet = () => { }); getClient()?.actions.buyToken({ - items: [ - { fillType: 'mint', collection: mintCollection.contract, quantity }, - ], + items: [{ fillType: 'mint', collection: mintCollection.id!, quantity }], wallet: signer!, chainId: networkObj.id, precheck: true, @@ -444,10 +442,7 @@ const MintSheet = () => { to: item.data?.to, from: item.data?.from, data: item.data?.data, - value: multiply( - mintCollection.mintStatus?.price || '0', - quantity - ), + value: multiply(price || '0', quantity), }; const gas = await estimateGas(tx, provider); if (gas) { @@ -470,8 +465,8 @@ const MintSheet = () => { }, [ accountAddress, currentNetwork, - mintCollection.contract, - mintCollection.mintStatus?.price, + mintCollection.id, + price, quantity, updateTxFee, ]); @@ -483,17 +478,17 @@ const MintSheet = () => { }); const deployerDisplay = abbreviations.address( - mintCollection.deployer || '', + mintCollection.creator || '', 4, 6 ); const contractAddressDisplay = `${abbreviations.address( - mintCollection.contract || '', + mintCollection.id || '', 4, 6 )} 􀄯`; const mintPriceDisplay = convertRawAmountToBalance( - multiply(mintCollection.mintStatus?.price || '0', quantity), + multiply(price || '0', quantity), { decimals: 18, symbol: 'ETH', @@ -529,13 +524,13 @@ const MintSheet = () => { yPosition={yPosition} > - - + + + } + > @@ -578,9 +573,9 @@ const MintSheet = () => { )} @@ -634,10 +629,12 @@ const MintSheet = () => { {/* @ts-ignore */} { nPress={()=> ethereumUtils.openAddressInBlockExplorer(mintCollection?.contract, currentNetwork)} value={contractAddressDisplay} color={imageColor} */} - - - - - - - - {mintCollection?.totalMints && ( + {mintCollection.description && ( + + + {`Description`} + + + {mintCollection.description} + + + )} + + {mintCollection?.tokenCount && ( { size="17pt" weight="medium" > - {`${mintCollection.totalMints} NFTs`} + {`${mintCollection.tokenCount} NFTs`} } /> )} - {mintCollection?.firstEvent && ( + {mintCollection?.createdAt && ( { size="17pt" weight="medium" > - {getFormattedDate(mintCollection?.firstEvent)} + {getFormattedDate(mintCollection?.createdAt)} } /> )} - {mintCollection?.lastEvent && ( + {false && ( { /> )} - {mintCollection?.maxSupply && ( + {false && ( { /> )} - {mintCollection?.contract && ( + {mintCollection?.id && ( { ethereumUtils.openAddressInBlockExplorer( - mintCollection.contract, + mintCollection.id!, currentNetwork ) } @@ -809,8 +815,9 @@ const MintSheet = () => { } /> - - + + + From 42fa90c78312155de88e2c5b2c84edb30473e474 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Wed, 13 Sep 2023 14:57:00 -0400 Subject: [PATCH 04/44] nftOffers -> reservior --- src/App.js | 2 +- src/components/cards/NFTOffersCard/index.tsx | 5 ++++- .../utils.ts => reservoir/client.ts} | 19 +++++++++++-------- .../index.ts => reservoir/nftOffersQuery.ts} | 0 src/screens/NFTOffersSheet/index.tsx | 5 ++++- src/screens/NFTSingleOfferSheet/index.tsx | 2 +- 6 files changed, 21 insertions(+), 12 deletions(-) rename src/resources/{nftOffers/utils.ts => reservoir/client.ts} (76%) rename src/resources/{nftOffers/index.ts => reservoir/nftOffersQuery.ts} (100%) diff --git a/src/App.js b/src/App.js index 6d73ba536b4..c2facc25a93 100644 --- a/src/App.js +++ b/src/App.js @@ -81,7 +81,7 @@ import { migrate } from '@/migrations'; import { initListeners as initWalletConnectListeners } from '@/walletConnect'; import { saveFCMToken } from '@/notifications/tokens'; import branch from 'react-native-branch'; -import { initializeReservoirClient } from '@/resources/nftOffers/utils'; +import { initializeReservoirClient } from '@/resources/reservoir/client'; if (__DEV__) { reactNativeDisableYellowBox && LogBox.ignoreAllLogs(); diff --git a/src/components/cards/NFTOffersCard/index.tsx b/src/components/cards/NFTOffersCard/index.tsx index d7ec84b44c9..64fee7d0cec 100644 --- a/src/components/cards/NFTOffersCard/index.tsx +++ b/src/components/cards/NFTOffersCard/index.tsx @@ -19,7 +19,10 @@ import { ShimmerAnimation, } from '@/components/animations'; import { useAccountSettings, useDimensions } from '@/hooks'; -import { nftOffersQueryKey, useNFTOffers } from '@/resources/nftOffers'; +import { + nftOffersQueryKey, + useNFTOffers, +} from '@/resources/reservoir/nftOffersQuery'; import { convertAmountToNativeDisplay } from '@/helpers/utilities'; import * as i18n from '@/languages'; import Animated, { diff --git a/src/resources/nftOffers/utils.ts b/src/resources/reservoir/client.ts similarity index 76% rename from src/resources/nftOffers/utils.ts rename to src/resources/reservoir/client.ts index 8555b933701..a9b13d0d22a 100644 --- a/src/resources/nftOffers/utils.ts +++ b/src/resources/reservoir/client.ts @@ -7,6 +7,9 @@ import { import { getBaseNetworkObject } from '@/networks/base'; import { getArbitrumNetworkObject } from '@/networks/arbitrum'; import { getOptimismNetworkObject } from '@/networks/optimism'; +import { getZoraNetworkObject } from '@/networks/zora'; +import { getPolygonNetworkObject } from '@/networks/polygon'; +import { getMainnetNetworkObject } from '@/networks/mainnet'; const RESERVOIR_API_KEY = IS_PROD ? RESERVOIR_API_KEY_PROD @@ -16,39 +19,39 @@ export function initializeReservoirClient() { createClient({ chains: [ { - id: 1, + id: getMainnetNetworkObject().id, baseApiUrl: 'https://api.reservoir.tools', active: true, apiKey: RESERVOIR_API_KEY, }, { - id: 137, + id: getPolygonNetworkObject().id, baseApiUrl: 'https://api-polygon.reservoir.tools', - active: true, + active: false, apiKey: RESERVOIR_API_KEY, }, { - id: 7777777, + id: getZoraNetworkObject().id, baseApiUrl: 'https://api-zora.reservoir.tools', - active: true, + active: false, apiKey: RESERVOIR_API_KEY, }, { id: getBaseNetworkObject().id, baseApiUrl: 'https://api-base.reservoir.tools', - active: true, + active: false, apiKey: RESERVOIR_API_KEY, }, { id: getOptimismNetworkObject().id, baseApiUrl: 'https://api-optimism.reservoir.tools', - active: true, + active: false, apiKey: RESERVOIR_API_KEY, }, { id: getArbitrumNetworkObject().id, baseApiUrl: 'https://api-arbitrum.reservoir.tools', - active: true, + active: false, apiKey: RESERVOIR_API_KEY, }, ], diff --git a/src/resources/nftOffers/index.ts b/src/resources/reservoir/nftOffersQuery.ts similarity index 100% rename from src/resources/nftOffers/index.ts rename to src/resources/reservoir/nftOffersQuery.ts diff --git a/src/screens/NFTOffersSheet/index.tsx b/src/screens/NFTOffersSheet/index.tsx index c2adc2629d3..0615fe8f386 100644 --- a/src/screens/NFTOffersSheet/index.tsx +++ b/src/screens/NFTOffersSheet/index.tsx @@ -17,7 +17,10 @@ import { FakeOfferRow, OfferRow } from './OfferRow'; import { useAccountProfile, useDimensions } from '@/hooks'; import { ImgixImage } from '@/components/images'; import { ContactAvatar } from '@/components/contacts'; -import { nftOffersQueryKey, useNFTOffers } from '@/resources/nftOffers'; +import { + nftOffersQueryKey, + useNFTOffers, +} from '@/resources/reservoir/nftOffersQuery'; import { convertAmountToNativeDisplay } from '@/helpers/utilities'; import { SortMenu } from '@/components/nft-offers/SortMenu'; import * as i18n from '@/languages'; diff --git a/src/screens/NFTSingleOfferSheet/index.tsx b/src/screens/NFTSingleOfferSheet/index.tsx index e1dc3dfe446..d462ab6d2fe 100644 --- a/src/screens/NFTSingleOfferSheet/index.tsx +++ b/src/screens/NFTSingleOfferSheet/index.tsx @@ -49,7 +49,7 @@ import { Network } from '@/helpers'; import { getNetworkObj } from '@/networks'; import { CardSize } from '@/components/unique-token/CardSize'; import { queryClient } from '@/react-query'; -import { nftOffersQueryKey } from '@/resources/nftOffers'; +import { nftOffersQueryKey } from '@/resources/reservoir/nftOffersQuery'; const NFT_IMAGE_HEIGHT = 160; const TWO_HOURS_MS = 2 * 60 * 60 * 1000; From 7bc184496cab304a5c098a9e48a3bc2e0230696f Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 18 Sep 2023 17:54:50 -0400 Subject: [PATCH 05/44] pending tx padding --- src/components/coin-row/FastTransactionStatusBadge.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/coin-row/FastTransactionStatusBadge.tsx b/src/components/coin-row/FastTransactionStatusBadge.tsx index 8e961bf28b6..d22b6802031 100644 --- a/src/components/coin-row/FastTransactionStatusBadge.tsx +++ b/src/components/coin-row/FastTransactionStatusBadge.tsx @@ -143,6 +143,10 @@ const StatusProps = { marginTop: ios ? -3 : -5, }, }, + [TransactionStatusTypes.minting]: { + marginRight: 4, + marginTop: ios ? 1 : 0, + }, }; const sx = StyleSheet.create({ From aa082320e86a8fa60a6eab9e6de12aad70620ad9 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 18 Sep 2023 17:55:04 -0400 Subject: [PATCH 06/44] i18n --- src/languages/_english.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/languages/_english.json b/src/languages/_english.json index 6bd2a623fb4..bc20c6cc1ff 100644 --- a/src/languages/_english.json +++ b/src/languages/_english.json @@ -1084,7 +1084,15 @@ }, "mints":{ "hold_to_mint": "Hold To Mint", + "minting": "Minting...", + "minted": "Minted", + "mint_unavailable": "Mint Unavailable", + "error_minting": "Error Minting", "mint_price": "Mint Price", + "free": "Free", + "by": "By", + "unknown": "unknown", + "description": "Description", "total_minted": "Total Minted", "first_event": "First Event", "last_event": "Last Event", From 5088ca91cc6de0c835d8a98cb35d1907daaaa37f Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 18 Sep 2023 17:55:27 -0400 Subject: [PATCH 07/44] analytics WIP --- src/analytics/event.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/analytics/event.ts b/src/analytics/event.ts index 38cec79df99..da15f4b0558 100644 --- a/src/analytics/event.ts +++ b/src/analytics/event.ts @@ -78,9 +78,13 @@ export const event = { nftOffersSelectedSortCriterion: 'Selected NFT Offers Sort Criterion', nftOffersAcceptedOffer: 'Accepted NFT Offer', + nftMintsOpenedSheet: 'Opened NFT Mint Sheet', + nftMintsMintedNFT: 'Minted NFT', + poapsOpenedMintSheet: 'Opened POAP mint sheet', poapsMintedPoap: 'Minted POAP', poapsViewedOnPoap: 'Viewed POAP on poap.gallery', + positionsOpenedSheet: 'Opened position Sheet', positionsOpenedExternalDapp: 'Viewed external dapp', } as const; From 5a78dda3656a446949425963bcc9670c63ea627e Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 18 Sep 2023 17:56:39 -0400 Subject: [PATCH 08/44] api updates + clean up --- src/screens/mints/MintSheet.tsx | 394 +++++++++++++------------------- 1 file changed, 154 insertions(+), 240 deletions(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 78478de66ca..1fef21d47b8 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -1,8 +1,6 @@ import { BlurView } from '@react-native-community/blur'; import React, { - ReactNode, - ReducerState, useCallback, useEffect, useMemo, @@ -10,9 +8,8 @@ import React, { useRef, useState, } from 'react'; -import { ColorValue, Linking, View } from 'react-native'; +import { View } from 'react-native'; import { useSharedValue } from 'react-native-reanimated'; - import useWallets from '../../hooks/useWallets'; import { GasSpeedButton } from '@/components/gas'; import { Execute, getClient } from '@reservoir0x/reservoir-sdk'; @@ -20,31 +17,18 @@ import { privateKeyToAccount } from 'viem/accounts'; import { createWalletClient, http } from 'viem'; import { dataAddNewTransaction } from '@/redux/data'; import { HoldToAuthorizeButton } from '@/components/buttons'; - import Routes from '@/navigation/routesNames'; - import ImgixImage from '../../components/images/ImgixImage'; -import { - SheetActionButton, - SheetActionButtonRow, - SlackSheet, -} from '../../components/sheet'; +import { SlackSheet } from '../../components/sheet'; import { CardSize } from '../../components/unique-token/CardSize'; import { - Bleed, Box, ColorModeProvider, Column, Columns, - DebugLayout, - Heading, - HeadingProps, Inline, Inset, - Row, - Rows, Separator, - Space, Stack, Text, } from '@/design-system'; @@ -59,59 +43,36 @@ import { useNavigation } from '@/navigation'; import styled from '@/styled-thing'; import { position } from '@/styles'; import { useTheme } from '@/theme'; -import { - CoinIcon, - abbreviations, - ethereumUtils, - gasUtils, - watchingAlert, -} from '@/utils'; +import { CoinIcon, abbreviations, ethereumUtils, watchingAlert } from '@/utils'; import { usePersistentDominantColorFromImage } from '@/hooks/usePersistentDominantColorFromImage'; import { maybeSignUri } from '@/handlers/imgix'; import { ButtonPressAnimation } from '@/components/animations'; import { useFocusEffect, useRoute } from '@react-navigation/native'; import { ReservoirCollection } from '@/graphql/__generated__/arcDev'; import { format } from 'date-fns'; -import { arcClient, arcDevClient } from '@/graphql'; -import Spinner from '@/components/Spinner'; -import { delay } from '@/utils/delay'; import { useLegacyNFTs } from '@/resources/nfts'; import { ParsedAddressAsset, TransactionStatus, TransactionType, - UniqueAsset, } from '@/entities'; -import { IS_DEV, IS_IOS } from '@/env'; import * as i18n from '@/languages'; -import { PoapMintError } from '@/utils/poaps'; import { analyticsV2 } from '@/analytics'; import { event } from '@/analytics/event'; import { ETH_ADDRESS, ETH_SYMBOL, ethUnits } from '@/references'; import { RainbowNetworks, getNetworkObj } from '@/networks'; import { Network } from '@/networks/types'; -import { UniqueTokenImage } from '@/components/unique-token'; import { fetchReverseRecord } from '@/handlers/ens'; import { ContactAvatar } from '@/components/contacts'; -import ImageAvatar from '@/components/contacts/ImageAvatar'; + import { addressHashedColorIndex } from '@/utils/profileUtils'; import { loadPrivateKey } from '@/model/wallet'; -import { current } from 'immer'; import { ChainBadge } from '@/components/coin-icon'; -import { - convertRawAmountToBalance, - handleSignificantDecimals, - multiply, -} from '@/helpers/utilities'; +import { convertRawAmountToBalance, multiply } from '@/helpers/utilities'; import { RainbowError, logger } from '@/logger'; import { useDispatch } from 'react-redux'; -import { DOGConfetti } from '@/components/floating-emojis/DOGConfetti'; import { QuantityButton } from './components/QuantityButton'; -import { - estimateGas, - estimateGasLimit, - getProviderForNetwork, -} from '@/handlers/web3'; +import { estimateGas, getProviderForNetwork } from '@/handlers/web3'; const NFT_IMAGE_HEIGHT = 250; @@ -189,56 +150,45 @@ const MintSheet = () => { const { height: deviceHeight, width: deviceWidth } = useDimensions(); const { navigate, goBack } = useNavigation(); const dispatch = useDispatch(); - const { colors, isDarkMode, lightScheme } = useTheme(); + const { colors, isDarkMode } = useTheme(); const { isReadOnlyWallet } = useWallets(); const currentNetwork = RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || Network.mainnet; - const didErrorRef = useRef(false); - const didCompleteRef = useRef(false); const txsRef = useRef([]); - const [nativeAsset, setNativeAsset] = useState< - ParsedAddressAsset | undefined - >(); + const maxMintsPerWallet = + mintCollection.mintStages?.find(item => item?.stage === 'public-sale') + ?.maxMintsPerWallet || 3; + console.log(maxMintsPerWallet); + const [quantity, setQuantity] = useReducer( (quantity: number, increment: number) => { if (quantity === 1 && increment === -1) { return quantity; } + if ( + maxMintsPerWallet && + quantity === maxMintsPerWallet && + increment === 1 + ) { + return quantity; + } return quantity + increment; }, 1 ); - useEffect(() => { - const getNativeAsset = async () => { - const asset = await ethereumUtils.getNativeAssetForNetwork( - currentNetwork, - accountAddress - ); - if (asset) { - setNativeAsset(asset); - } - }; - - getNativeAsset(); - }, [accountAddress, currentNetwork]); - const { - gasLimit, updateTxFee, startPollingGasFees, stopPollingGasFees, isSufficientGas, - selectedGasFee, isValidGas, - updateDefaultGasLimit, - updateGasFeeOption, - } = useGas({ nativeAsset }); + } = useGas(); const insufficientEth = isSufficientGas === false && isValidGas; @@ -250,19 +200,13 @@ const MintSheet = () => { const imageUrl = maybeSignUri(mintCollection.image || ''); const { result: aspectRatio } = usePersistentAspectRatio(imageUrl || ''); - const [errorCode, setErrorCode] = useState( - undefined - ); - - const [nft, setNft] = useState(null); - const getFormattedDate = (date: string) => { return format(new Date(date), 'MMMM dd, yyyy'); }; const price = mintCollection.mintStages?.find( item => item?.stage === 'public-sale' )?.price?.amount?.raw; - const isMinting = mintCollection.isMintingPublicSale; + const isMintingAvailable = mintCollection.isMintingPublicSale; const imageColor = usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; @@ -270,97 +214,6 @@ const MintSheet = () => { const sheetRef = useRef(); const yPosition = useSharedValue(0); - const actionOnPress = useCallback(async () => { - logger.info('Minting NFT', { name: mintCollection.name }); - - const privateKey = await loadPrivateKey(accountAddress, false); - // @ts-ignore - const account = privateKeyToAccount(privateKey); - const networkObj = getNetworkObj(currentNetwork); - const signer = createWalletClient({ - account, - chain: networkObj, - transport: http(networkObj.rpc), - }); - - getClient()?.actions.buyToken({ - items: [{ fillType: 'mint', collection: mintCollection.id!, quantity }], - wallet: signer!, - chainId: networkObj.id, - onProgress: (steps: Execute['steps']) => { - steps.forEach(step => { - if (step.error && !didErrorRef.current) { - didErrorRef.current = true; - logger.error(new RainbowError(`Error minting NFT: ${step.error}`)); - // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { - // status: 'failed', - // ...analyticsEventObject, - // }); - return; - } - step.items?.forEach(item => { - if ( - item.txHash && - !txsRef.current.includes(item.txHash) && - item.status === 'incomplete' - ) { - const tx = { - to: item.data?.to, - from: item.data?.from, - hash: item.txHash, - network: currentNetwork, - amount: mintPriceDisplay.amount, - asset: { - address: ETH_ADDRESS, - symbol: ETH_SYMBOL, - }, - nft: { - predominantColor: imageColor, - collection: { - image: imageUrl, - }, - lowResUrl: imageUrl, - name: mintCollection.name, - }, - type: TransactionType.mint, - status: TransactionStatus.minting, - }; - - if (tx) { - console.log({ txNetwork: tx.network }); - txsRef.current.push(tx.hash); - // @ts-ignore TODO: fix when we overhaul tx list, types are not good - dispatch(dataAddNewTransaction(tx)); - navigate(Routes.PROFILE_SCREEN); - } - } - }); - }); - }, - }); - - // if (claimStatus === 'claimed') { - // if (nft) { - // navigate(Routes.EXPANDED_ASSET_SHEET, { - // asset: nft, - // backgroundOpacity: 1, - // cornerRadius: 'device', - // external: false, - // springDamping: 1, - // topOffset: 0, - // transitionDuration: 0.25, - // type: 'unique_token', - // }); - // } - // } else { - // if (poapMintType === 'secretWord') { - // await claimPoapBySecret(); - // } else { - // await claimPoapByQrHash(); - // } - // } - }, []); - useEffect(() => { // const nft = nfts.find( // item => item.image_original_url === poapEvent.imageUrl @@ -371,24 +224,12 @@ const MintSheet = () => { // } }, [imageUrl, nfts]); - const getErrorMessage = () => { - if (errorCode === 'LIMIT_EXCEEDED') { - return i18n.t(i18n.l.poaps.error_messages.limit_exceeded); - } else if (errorCode === 'EVENT_EXPIRED') { - return i18n.t(i18n.l.poaps.error_messages.event_expired); - } - return i18n.t(i18n.l.poaps.error_messages.event_expired); - }; - - useFocusEffect(() => { - //analytics go here - }); - useEffect(() => { const fetchENSName = async (address: string) => { const ensName = await fetchReverseRecord(address); setENSName(ensName); }; + if (mintCollection.creator) { fetchENSName(mintCollection.creator); } @@ -426,10 +267,9 @@ const MintSheet = () => { precheck: true, onProgress: async (steps: Execute['steps']) => { steps.forEach(step => { - if (step.error && !didErrorRef.current) { - didErrorRef.current = true; + if (step.error) { logger.error( - new RainbowError(`Error minting NFT: ${step.error}`) + new RainbowError(`NFT Mints: Gas Step Error: ${step.error}`) ); // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { // status: 'failed', @@ -438,6 +278,7 @@ const MintSheet = () => { return; } step.items?.forEach(async item => { + // could add safety here if unable to calc gas limit const tx = { to: item.data?.to, from: item.data?.from, @@ -447,13 +288,8 @@ const MintSheet = () => { const gas = await estimateGas(tx, provider); if (gas) { updateTxFee(gas, null); - } - { - updateTxFee( - ethUnits.basic_swap, - null, - ethUnits.default_l1_gas_fee_optimism_swap - ); + } else { + console.log('ERROR CALCULATING GAS'); } }); }); @@ -472,6 +308,9 @@ const MintSheet = () => { ]); const [ensName, setENSName] = useState(''); + const [mintStatus, setMintStatus] = useState< + 'none' | 'minting' | 'minted' | 'error' + >('none'); const { data: ensAvatar } = useENSAvatar(ensName, { enabled: Boolean(ensName), @@ -482,11 +321,13 @@ const MintSheet = () => { 4, 6 ); + const contractAddressDisplay = `${abbreviations.address( mintCollection.id || '', 4, 6 )} 􀄯`; + const mintPriceDisplay = convertRawAmountToBalance( multiply(price || '0', quantity), { @@ -495,6 +336,115 @@ const MintSheet = () => { } // abbreviate if amount is >= 10,000 ); + + const actionOnPress = useCallback(async () => { + if (isReadOnlyWallet) { + watchingAlert(); + return; + } + + logger.info('Minting NFT', { name: mintCollection.name }); + setMintStatus('minting'); + + const privateKey = await loadPrivateKey(accountAddress, false); + // @ts-ignore + const account = privateKeyToAccount(privateKey); + const networkObj = getNetworkObj(currentNetwork); + const signer = createWalletClient({ + account, + chain: networkObj, + transport: http(networkObj.rpc), + }); + + getClient()?.actions.buyToken({ + items: [{ fillType: 'mint', collection: mintCollection.id!, quantity }], + wallet: signer!, + chainId: networkObj.id, + onProgress: (steps: Execute['steps']) => { + steps.forEach(step => { + if (step.error) { + logger.error(new RainbowError(`Error minting NFT: ${step.error}`)); + // show alert + setMintStatus('error'); + // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { + // status: 'failed', + // ...analyticsEventObject, + // }); + return; + } + step.items?.forEach(item => { + if ( + item.txHash && + !txsRef.current.includes(item.txHash) && + item.status === 'incomplete' + ) { + const tx = { + to: item.data?.to, + from: item.data?.from, + hash: item.txHash, + network: currentNetwork, + amount: mintPriceDisplay.amount, + asset: { + address: ETH_ADDRESS, + symbol: ETH_SYMBOL, + }, + nft: { + predominantColor: imageColor, + collection: { + image: imageUrl, + }, + lowResUrl: imageUrl, + name: mintCollection.name, + }, + type: TransactionType.mint, + status: TransactionStatus.minting, + }; + + txsRef.current.push(tx.hash); + + // @ts-ignore TODO: fix when we overhaul tx list, types are not good + dispatch(dataAddNewTransaction(tx)); + navigate(Routes.PROFILE_SCREEN); + setMintStatus('minted'); + } + }); + }); + }, + }); + }, [ + accountAddress, + currentNetwork, + dispatch, + imageColor, + imageUrl, + isReadOnlyWallet, + mintCollection.id, + mintCollection.name, + mintPriceDisplay.amount, + navigate, + quantity, + ]); + + const buttonLabel = useMemo(() => { + if (!isMintingAvailable) { + return i18n.t(i18n.l.mints.mint_unavailable); + } + + if (insufficientEth) { + return i18n.t(i18n.l.button.confirm_exchange.insufficient_eth); + } + + if (mintStatus === 'minting') { + return i18n.t(i18n.l.mints.minting); + } else if (mintStatus === 'minted') { + return i18n.t(i18n.l.mints.minted); + } else if (mintStatus === 'error') { + return i18n.t(i18n.l.mints.error_minting); + } + + return i18n.t(i18n.l.mints.hold_to_mint); + }, [insufficientEth, isMintingAvailable, mintStatus]); + return ( <> {ios && ( @@ -560,7 +510,7 @@ const MintSheet = () => { - {`By `} + {`${i18n.t(i18n.l.mints.by)} `} {ensAvatar?.imageUrl ? ( @@ -580,7 +530,11 @@ const MintSheet = () => { /> )} - {` ${ensName || deployerDisplay || 'unknown'}`} + {` ${ + ensName || + deployerDisplay || + i18n.t(i18n.l.mints.unknown) + }`} @@ -600,7 +554,7 @@ const MintSheet = () => { size="13pt" weight="medium" > - {'Mint Price'} + {i18n.t(i18n.l.mints.mint_price)} { weight="bold" > {mintPriceDisplay.amount === '0' - ? 'Free' + ? i18n.t(i18n.l.mints.free) : mintPriceDisplay.display} @@ -629,17 +583,11 @@ const MintSheet = () => { {/* @ts-ignore */} { {mintCollection.description && ( - {`Description`} + {i18n.t(i18n.l.mints.description)} { /> )} - {false && ( - - {getFormattedDate(mintCollection?.lastEvent)} - - } - /> - )} - - {false && ( - - {`${mintCollection.maxSupply} NFTs`} - - } - /> - )} - {mintCollection?.id && ( Date: Tue, 19 Sep 2023 13:58:01 -0400 Subject: [PATCH 09/44] prettier & revert gascard --- src/components/cards/GasCard.tsx | 116 ++++++++++-------- src/components/contacts/ContactAvatar.js | 5 +- .../transactions/transactionStatus.ts | 2 +- src/graphql/config.js | 3 +- src/languages/en_US.json | 2 +- src/languages/fr_FR.json | 2 +- src/navigation/Routes.ios.tsx | 2 +- .../mints/components/QuantityButton.tsx | 109 ++++++++-------- 8 files changed, 132 insertions(+), 109 deletions(-) diff --git a/src/components/cards/GasCard.tsx b/src/components/cards/GasCard.tsx index 5712d5c8ad7..fa1f34ab330 100644 --- a/src/components/cards/GasCard.tsx +++ b/src/components/cards/GasCard.tsx @@ -2,7 +2,7 @@ import AnimateNumber from '@bankify/react-native-animate-number'; import { useIsFocused } from '@react-navigation/native'; import * as i18n from '@/languages'; -import { isNaN, truncate } from 'lodash'; +import { isNaN } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Animated, { Easing, @@ -12,8 +12,6 @@ import Animated, { withSpring, withTiming, } from 'react-native-reanimated'; -import Routes from '@/navigation/routesNames'; - import { AccentColorProvider, Box, @@ -22,33 +20,15 @@ import { Text, } from '@/design-system'; import { add } from '@/helpers/utilities'; -import { useAccountSettings } from '@/hooks'; +import { useGas } from '@/hooks'; import { gasUtils } from '@/utils'; import { GenericCard, SQUARE_CARD_SIZE } from './GenericCard'; -import { arcClient, arcDevClient } from '@/graphql'; -import { getZoraNetworkObject } from '@/networks/zora'; -import { useNavigation } from '@/navigation'; -import { getOptimismNetworkObject } from '@/networks/optimism'; type AnimationConfigOptions = { duration: number; easing: Animated.EasingFunction; }; -const mints = { - OP: { - addy: '0xcB0Bb5D835A47584fdA53F57bb4193B28d2738dB', - chain: getOptimismNetworkObject().id - }, - Zora: { - addy: '0x81eCE04F2aFadCfb1c06f8331de8cba253564Ec1', - chain: getZoraNetworkObject().id - }, - Eth: { - addy: '0x932261f9Fc8DA46C4a22e31B45c4De60623848bF', - chain: 1 - } -} const TRANSLATIONS = i18n.l.cards.gas; const containerConfig = { @@ -67,34 +47,56 @@ const fadeOutConfig: AnimationConfigOptions = { }; export const GasCard = () => { + const { + currentBlockParams, + gasFeeParamsBySpeed, + startPollingGasFees, + stopPollingGasFees, + } = useGas(); const isFocused = useIsFocused(); const [lastKnownGwei, setLastKnownGwei] = useState(''); - const { accountAddress} = useAccountSettings(); - const { navigate} = useNavigation(); const container = useSharedValue(1); const opacity = useSharedValue(0); const scale = useSharedValue(0); - + // Listen to gas prices + useEffect(() => { + if (isFocused) { + startPollingGasFees(); + } else { + stopPollingGasFees(); + } + }, [isFocused, startPollingGasFees, stopPollingGasFees]); const { NORMAL } = gasUtils; - - + const currentGwei = useMemo( + () => + add( + currentBlockParams?.baseFeePerGas?.gwei, + gasFeeParamsBySpeed[NORMAL]?.maxPriorityFeePerGas?.gwei + ), + [NORMAL, currentBlockParams?.baseFeePerGas?.gwei, gasFeeParamsBySpeed] + ); + const isCurrentGweiLoaded = currentGwei && Number(currentGwei) > 0; const renderGweiText = useCallback( // @ts-expect-error passed to an untyped JS component animatedNumber => { const priceText = - 'lol' + animatedNumber === 0 + ? isCurrentGweiLoaded + ? Math.round(Number(currentGwei)) + : Math.round(Number(lastKnownGwei)) || '􀖇' + : animatedNumber; return ( {priceText} ); }, - [] + [currentGwei, isCurrentGweiLoaded, lastKnownGwei] ); // @ts-expect-error passed to an untyped JS component @@ -106,21 +108,24 @@ export const GasCard = () => { } }, []); - const handlePress = useCallback(async () => { - try{ - navigate(Routes.HARDWARE_WALLET_TX_NAVIGATOR); - // navigate(Routes.NOTIFICATIONS_PROMO_SHEET); - // const res = await arcDevClient.getSingleCollection({walletAddress: accountAddress, contractAddress: mints.Zora.addy, chain: mints.Zora.chain}) - // console.log('posty'); - // console.log({res}); - // navigate(Routes.MINT_SHEET, {collection: res.getSingleCollection?.collection}) - - } catch (e) -{ - console.log(e) -} - - }, [accountAddress, navigate]); + const handlePress = useCallback(() => { + opacity.value = 0; + scale.value = 0; + container.value = withSequence( + withSpring(1.04, containerConfig), + withSpring(1, pulseConfig) + ); + opacity.value = withSequence( + withSpring(1, pulseConfig), + withTiming(0, fadeOutConfig), + withTiming(0, { duration: 0 }) + ); + scale.value = withSequence( + withSpring(1, pulseConfig), + withTiming(1, fadeOutConfig), + withTiming(0, { duration: 0 }) + ); + }, [container, opacity, scale]); const getColorForGwei = (currentGwei: string, lastKnownGwei: string) => { 'worklet'; @@ -164,9 +169,10 @@ export const GasCard = () => { useEffect(() => { if ( - true + isCurrentGweiLoaded && + Math.round(Number(currentGwei)) !== Math.round(Number(lastKnownGwei)) ) { - setLastKnownGwei('66'); + setLastKnownGwei(currentGwei); opacity.value = 0; scale.value = 0; container.value = withSequence( @@ -186,6 +192,8 @@ export const GasCard = () => { } }, [ container, + currentGwei, + isCurrentGweiLoaded, lastKnownGwei, opacity, scale, @@ -199,10 +207,10 @@ export const GasCard = () => { }, ], }; - }, [ lastKnownGwei]); + }, [currentGwei, lastKnownGwei]); const pulseStyle = useAnimatedStyle(() => { - const color = getColorForGwei('66', lastKnownGwei); + const color = getColorForGwei(currentGwei, lastKnownGwei); return { backgroundColor: color, @@ -216,13 +224,13 @@ export const GasCard = () => { ], width: SQUARE_CARD_SIZE, }; - }, [ lastKnownGwei]); + }, [currentGwei, lastKnownGwei]); return ( { interval={2} renderContent={renderGweiText} timing={(t: number) => 1 - --t * t * t * t} - value={'66' || lastKnownGwei} + value={currentGwei || lastKnownGwei} /> - { !lastKnownGwei + {!isCurrentGweiLoaded && !lastKnownGwei ? '' : i18n.t(TRANSLATIONS.gwei)} @@ -251,14 +259,14 @@ export const GasCard = () => { - {getCurrentPriceComparison('66', lastKnownGwei)} + {getCurrentPriceComparison(currentGwei, lastKnownGwei)} diff --git a/src/components/contacts/ContactAvatar.js b/src/components/contacts/ContactAvatar.js index 68af91bfc10..3f29234abee 100644 --- a/src/components/contacts/ContactAvatar.js +++ b/src/components/contacts/ContactAvatar.js @@ -118,7 +118,10 @@ const ContactAvatar = ({ color, size = 'medium', value, ...props }) => { ]); const { isDarkMode } = useTheme(); - const shadows = useMemo(() => buildShadows(color, size, props?.forceDarkMode || isDarkMode, colors), [color, size, props?.forceDarkMode, isDarkMode, colors]); + const shadows = useMemo( + () => buildShadows(color, size, props?.forceDarkMode || isDarkMode, colors), + [color, size, props?.forceDarkMode, isDarkMode, colors] + ); const backgroundColor = typeof color === 'number' diff --git a/src/entities/transactions/transactionStatus.ts b/src/entities/transactions/transactionStatus.ts index 0e15919e55f..0449bf04cfa 100644 --- a/src/entities/transactions/transactionStatus.ts +++ b/src/entities/transactions/transactionStatus.ts @@ -41,7 +41,7 @@ export default { depositing: 'depositing', dropped: 'dropped', failed: 'failed', - minted:'minted', + minted: 'minted', minting: 'minting', purchased: 'purchased', purchasing: 'purchasing', diff --git a/src/graphql/config.js b/src/graphql/config.js index dce581c0510..1789dff4817 100644 --- a/src/graphql/config.js +++ b/src/graphql/config.js @@ -18,8 +18,7 @@ exports.config = { schema: { method: 'GET', url: 'https://arc-graphql.rainbowdotme.workers.dev/graphql', - headers: { - }, + headers: {}, }, }, arcDev: { diff --git a/src/languages/en_US.json b/src/languages/en_US.json index 1f74d2b3c2c..47a2e01f32e 100644 --- a/src/languages/en_US.json +++ b/src/languages/en_US.json @@ -1126,7 +1126,7 @@ "withdrawal_dropdown_label": "For", "withdrawal_input_label": "Get" }, - "mints":{ + "mints": { "hold_to_mint": "Hold To Mint", "minting": "Minting...", "minted": "Minted", diff --git a/src/languages/fr_FR.json b/src/languages/fr_FR.json index 3e9d7eecd92..37a02a2bfa7 100644 --- a/src/languages/fr_FR.json +++ b/src/languages/fr_FR.json @@ -1126,7 +1126,7 @@ "withdrawal_dropdown_label": "pour", "withdrawal_input_label": "Obtenir" }, - "mints":{ + "mints": { "hold_to_mint": "Hold To Mint", "mint_price": "Mint Price", "total_minted": "Total Minted", diff --git a/src/navigation/Routes.ios.tsx b/src/navigation/Routes.ios.tsx index 0d7aa8fb783..71406e53407 100644 --- a/src/navigation/Routes.ios.tsx +++ b/src/navigation/Routes.ios.tsx @@ -215,7 +215,7 @@ function NativeStackNavigator() { name={Routes.POAP_SHEET} {...expandedAssetSheetConfigWithLimit} /> - ({ scaleTo: 0.75, }))({}); - - type StepButtonProps = { - type: 'plus' | 'minus', - onLongPress: () => void, - onLongPressEnded: () => void, - onPress: () => void, - shouldLongPressHoldPress: boolean, - buttonColor: string -} + type: 'plus' | 'minus'; + onLongPress: () => void; + onLongPressEnded: () => void; + onPress: () => void; + shouldLongPressHoldPress: boolean; + buttonColor: string; +}; const StepButton = ({ type, onLongPress, @@ -47,23 +45,35 @@ const StepButton = ({ shouldLongPressHoldPress={shouldLongPressHoldPress} useLateHaptic={false} > - - - {type === 'plus' ? '􀅼' : '􀅽'} - + + + {type === 'plus' ? '􀅼' : '􀅽'} + - ); }; - type StepButtonInputProps = { - value: number, - plusAction: ()=> void, - minusAction: ()=> void, - buttonColor: string -} + value: number; + plusAction: () => void; + minusAction: () => void; + buttonColor: string; +}; export function QuantityButton({ value, plusAction, @@ -130,34 +140,37 @@ export function QuantityButton({ return ( - - - - - - {value} - - - - + + + + {value} + + + ); } From 294b38c311fa2914a81cd8f5ea18f21d3baa29cc Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Tue, 19 Sep 2023 14:00:08 -0400 Subject: [PATCH 10/44] revert dev only endpoint --- src/graphql/config.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/graphql/config.js b/src/graphql/config.js index 1789dff4817..69bbea91490 100644 --- a/src/graphql/config.js +++ b/src/graphql/config.js @@ -17,8 +17,10 @@ exports.config = { document: './queries/arc.graphql', schema: { method: 'GET', - url: 'https://arc-graphql.rainbowdotme.workers.dev/graphql', - headers: {}, + url: 'https://arc-graphql.rainbow.me/graphql', + headers: { + 'x-api-key': 'ARC_GRAPHQL_API_KEY', + }, }, }, arcDev: { From c9eb904489af7acde56d2518adf70f90be10dec1 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Tue, 19 Sep 2023 14:02:39 -0400 Subject: [PATCH 11/44] graphql --- src/graphql/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 63c4dd57a63..8ea3ac47ffb 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -9,5 +9,5 @@ export const ensClient = getEnsSdk(getFetchRequester(config.ens)); export const metadataClient = getMetadataSdk( getFetchRequester(config.metadata) ); -export const arcClient = getArcDevSdk(getFetchRequester(config.arcDev)); +export const arcClient = getArcSdk(getFetchRequester(config.arcDev)); export const arcDevClient = getArcDevSdk(getFetchRequester(config.arcDev)); From 4dc00ea69204992aa5f2923c9e48de782eaf8867 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Tue, 19 Sep 2023 14:04:52 -0400 Subject: [PATCH 12/44] rm french strings --- src/languages/fr_FR.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/languages/fr_FR.json b/src/languages/fr_FR.json index 37a02a2bfa7..d8aa2903c86 100644 --- a/src/languages/fr_FR.json +++ b/src/languages/fr_FR.json @@ -1126,16 +1126,6 @@ "withdrawal_dropdown_label": "pour", "withdrawal_input_label": "Obtenir" }, - "mints": { - "hold_to_mint": "Hold To Mint", - "mint_price": "Mint Price", - "total_minted": "Total Minted", - "first_event": "First Event", - "last_event": "Last Event", - "max_supply": "Max Supply", - "contract": "Contract", - "network": "Network" - }, "nfts": { "selling": "Vente" }, From e21b9507d6612b2296f44f19833bd89f4ec02823 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Tue, 19 Sep 2023 14:05:44 -0400 Subject: [PATCH 13/44] android route --- src/navigation/Routes.android.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/navigation/Routes.android.tsx b/src/navigation/Routes.android.tsx index 15bb91817aa..918d3a84da4 100644 --- a/src/navigation/Routes.android.tsx +++ b/src/navigation/Routes.android.tsx @@ -81,6 +81,7 @@ import { NFTSingleOfferSheet } from '@/screens/NFTSingleOfferSheet'; import ShowSecretView from '@/screens/SettingsSheet/components/ShowSecretView'; import PoapSheet from '@/screens/mints/PoapSheet'; import { PositionSheet } from '@/screens/positions/PositionSheet'; +import MintSheet from '@/screens/mints/MintSheet'; const Stack = createStackNavigator(); const OuterStack = createStackNavigator(); @@ -252,6 +253,7 @@ function BSNavigator() { name={Routes.EXPANDED_ASSET_SHEET} /> + Date: Wed, 20 Sep 2023 23:18:51 -0400 Subject: [PATCH 14/44] analytics --- src/analytics/event.ts | 19 +++++++++++++++++ src/screens/mints/MintSheet.tsx | 36 +++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/analytics/event.ts b/src/analytics/event.ts index da15f4b0558..4a10b425240 100644 --- a/src/analytics/event.ts +++ b/src/analytics/event.ts @@ -1,6 +1,7 @@ import { CardType } from '@/components/cards/GenericCard'; import { LearnCategory } from '@/components/cards/utils/types'; import { FiatProviderName } from '@/entities/f2c'; +import { Network } from '@/networks/types'; /** * All events, used by `analytics.track()` @@ -79,6 +80,7 @@ export const event = { nftOffersAcceptedOffer: 'Accepted NFT Offer', nftMintsOpenedSheet: 'Opened NFT Mint Sheet', + nftMintsMintingNFT: 'Minting NFT', nftMintsMintedNFT: 'Minted NFT', poapsOpenedMintSheet: 'Opened POAP mint sheet', @@ -290,6 +292,23 @@ export type EventProperties = { rainbowFee: number; offerCurrency: { symbol: string; contractAddress: string }; }; + [event.nftMintsMintingNFT]: { + contract: string; + network: Network; + quantity: number; + collectionName: string; + }; + [event.nftMintsMintedNFT]: { + contract: string; + network: Network; + quantity: number; + collectionName: string; + }; + [event.nftMintsOpenedSheet]: { + contract: string; + network: Network; + collectionName: string; + }; [event.poapsMintedPoap]: { eventId: number; type: 'qrHash' | 'secretWord'; diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 1fef21d47b8..3baf73d75b5 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -214,15 +214,15 @@ const MintSheet = () => { const sheetRef = useRef(); const yPosition = useSharedValue(0); - useEffect(() => { - // const nft = nfts.find( - // item => item.image_original_url === poapEvent.imageUrl - // ); - // if (nft) { - // setClaimStatus('claimed'); - // setNft(nft); - // } - }, [imageUrl, nfts]); + useFocusEffect(() => { + if (mintCollection.name && mintCollection.id) { + analyticsV2.track(event.nftMintsOpenedSheet, { + collectionName: mintCollection?.name, + contract: mintCollection.id, + network: currentNetwork, + }); + } + }); useEffect(() => { const fetchENSName = async (address: string) => { @@ -334,7 +334,6 @@ const MintSheet = () => { decimals: 18, symbol: 'ETH', } - // abbreviate if amount is >= 10,000 ); const actionOnPress = useCallback(async () => { @@ -344,6 +343,12 @@ const MintSheet = () => { } logger.info('Minting NFT', { name: mintCollection.name }); + analyticsV2.track(event.nftMintsMintingNFT, { + collectionName: mintCollection.name || '', + contract: mintCollection.id || '', + network: currentNetwork, + quantity, + }); setMintStatus('minting'); const privateKey = await loadPrivateKey(accountAddress, false); @@ -364,12 +369,7 @@ const MintSheet = () => { steps.forEach(step => { if (step.error) { logger.error(new RainbowError(`Error minting NFT: ${step.error}`)); - // show alert setMintStatus('error'); - // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { - // status: 'failed', - // ...analyticsEventObject, - // }); return; } step.items?.forEach(item => { @@ -404,6 +404,12 @@ const MintSheet = () => { // @ts-ignore TODO: fix when we overhaul tx list, types are not good dispatch(dataAddNewTransaction(tx)); + analyticsV2.track(event.nftMintsMintedNFT, { + collectionName: mintCollection.name || '', + contract: mintCollection.id || '', + network: currentNetwork, + quantity, + }); navigate(Routes.PROFILE_SCREEN); setMintStatus('minted'); } From f071f6713abfd2b61a2bf27e9cf58a88699f0c21 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Wed, 27 Sep 2023 20:16:22 -0400 Subject: [PATCH 15/44] connect the dots --- src/components/cards/FeaturedMintCard.tsx | 8 +- .../cards/MintsCard/CollectionCell.tsx | 7 +- src/graphql/queries/arc.graphql | 2 +- src/languages/en_US.json | 3 +- src/resources/reservoir/mints.ts | 5 +- src/screens/MintsSheet/card/Card.tsx | 3 +- .../discover/components/DiscoverSearch.js | 16 +--- src/screens/mints/MintSheet.tsx | 93 ++++++++++++++----- .../mints/components/QuantityButton.tsx | 19 ++++ 9 files changed, 112 insertions(+), 44 deletions(-) diff --git a/src/components/cards/FeaturedMintCard.tsx b/src/components/cards/FeaturedMintCard.tsx index 02149cbca2e..73e290d9f60 100644 --- a/src/components/cards/FeaturedMintCard.tsx +++ b/src/components/cards/FeaturedMintCard.tsx @@ -29,6 +29,8 @@ import { IS_IOS } from '@/env'; import { Media } from '../Media'; import { analyticsV2 } from '@/analytics'; import * as i18n from '@/languages'; +import { navigateToMintCollection } from '@/resources/reservoir/mints'; +import { ethereumUtils } from '@/utils'; const IMAGE_SIZE = 111; @@ -123,7 +125,11 @@ export function FeaturedMintCard() { ), } ); - Linking.openURL(featuredMint.externalURL); + const network = ethereumUtils.getNetworkFromChainId( + featuredMint.chainId + ); + navigateToMintCollection(featuredMint.contract, network); + // Linking.openURL(featuredMint.externalURL); }} scaleTo={0.96} > diff --git a/src/components/cards/MintsCard/CollectionCell.tsx b/src/components/cards/MintsCard/CollectionCell.tsx index c9eae64f150..bbdf202b1fa 100644 --- a/src/components/cards/MintsCard/CollectionCell.tsx +++ b/src/components/cards/MintsCard/CollectionCell.tsx @@ -15,12 +15,13 @@ import { ButtonPressAnimation } from '@/components/animations'; import { useTheme } from '@/theme'; import { Linking, View } from 'react-native'; import { MintableCollection } from '@/graphql/__generated__/arc'; -import { getNetworkFromChainId } from '@/utils/ethereumUtils'; +import ethereumUtils, { getNetworkFromChainId } from '@/utils/ethereumUtils'; import { getNetworkObj } from '@/networks'; import { analyticsV2 } from '@/analytics'; import * as i18n from '@/languages'; import { IS_IOS } from '@/env'; import { ImgixImage } from '@/components/images'; +import { navigateToMintCollection } from '@/resources/reservoir/mints'; export const NFT_IMAGE_SIZE = 111; @@ -98,7 +99,9 @@ export function CollectionCell({ chainId: collection.chainId, priceInEth: amount, }); - Linking.openURL(collection.externalURL); + + const network = ethereumUtils.getNetworkFromChainId(collection.chainId); + navigateToMintCollection(collection.contract, network); }} style={{ width: NFT_IMAGE_SIZE }} > diff --git a/src/graphql/queries/arc.graphql b/src/graphql/queries/arc.graphql index ec6c0056377..59860a9415a 100644 --- a/src/graphql/queries/arc.graphql +++ b/src/graphql/queries/arc.graphql @@ -102,7 +102,7 @@ query claimPoapBySecretWord($walletAddress: String!, $secretWord: String!) { } query getReservoirCollection($contractAddress: String!, $chainId: Int!) { - getReserviorCollection(contractAddress: $contractAddress, chainId: $chainId) { + getReservoirCollection(contractAddress: $contractAddress, chainId: $chainId) { collection { id chainId diff --git a/src/languages/en_US.json b/src/languages/en_US.json index 25c9f772e23..0128bc01751 100644 --- a/src/languages/en_US.json +++ b/src/languages/en_US.json @@ -1152,11 +1152,12 @@ "withdrawal_dropdown_label": "For", "withdrawal_input_label": "Get" }, - "mints": { + "minting": { "hold_to_mint": "Hold To Mint", "minting": "Minting...", "minted": "Minted", "mint_unavailable": "Mint Unavailable", + "mint_on_mintdotfun": "􀮶 Mint on Mint.fun", "error_minting": "Error Minting", "mint_price": "Mint Price", "free": "Free", diff --git a/src/resources/reservoir/mints.ts b/src/resources/reservoir/mints.ts index a66e2ced7ac..0358d07e3a5 100644 --- a/src/resources/reservoir/mints.ts +++ b/src/resources/reservoir/mints.ts @@ -28,14 +28,15 @@ export const navigateToMintCollection = async ( contractAddress, chainId, }); - if (res?.getReserviorCollection?.collection) { + if (res?.getReservoirCollection?.collection) { Navigation.handleAction(Routes.MINT_SHEET, { - collection: res.getReserviorCollection?.collection, + collection: res.getReservoirCollection?.collection, }); } else { showAlert(); } } catch (e) { + console.log(e); showAlert(); } }; diff --git a/src/screens/MintsSheet/card/Card.tsx b/src/screens/MintsSheet/card/Card.tsx index 319f57f95ff..159df6c351b 100644 --- a/src/screens/MintsSheet/card/Card.tsx +++ b/src/screens/MintsSheet/card/Card.tsx @@ -26,6 +26,7 @@ import * as i18n from '@/languages'; import ChainBadge from '@/components/coin-icon/ChainBadge'; import { CoinIcon } from '@/components/coin-icon'; import { Network } from '@/helpers'; +import { navigateToMintCollection } from '@/resources/reservoir/mints'; export const NUM_NFTS = 3; @@ -129,7 +130,7 @@ export function Card({ collection }: { collection: MintableCollection }) { chainId: collection.chainId, priceInEth: price, }); - Linking.openURL(collection.externalURL); + navigateToMintCollection(collection.contract, network); }} style={{ borderRadius: 99, diff --git a/src/screens/discover/components/DiscoverSearch.js b/src/screens/discover/components/DiscoverSearch.js index d8e918cf4c2..40a29b08751 100644 --- a/src/screens/discover/components/DiscoverSearch.js +++ b/src/screens/discover/components/DiscoverSearch.js @@ -39,6 +39,7 @@ import { } from '@/utils/poaps'; import { arcDevClient } from '@/graphql'; import { getNetworkObj } from '@/networks'; +import { navigateToMintCollection } from '@/resources/reservoir/mints'; export const SearchContainer = styled(Row)({ height: '100%', @@ -149,6 +150,8 @@ export default function DiscoverSearch() { }, [searchQueryForPoap]); useEffect(() => { + // probably dont need this entry point but seems worth keeping? + // could do the same with zora, etc const checkAndHandleMint = async seachQueryForMint => { if (seachQueryForMint.includes('mint.fun')) { const mintdotfunURL = seachQueryForMint.split('https://mint.fun/'); @@ -160,18 +163,7 @@ export default function DiscoverSearch() { network = 'mainnet'; } const contractAddress = query.split('/')[1]; - const chainID = getNetworkObj(network).id; - console.log({ chainID, contractAddress }); - const res = await arcDevClient.getSingleCollection({ - walletAddress: accountAddress, - contractAddress, - chain: chainID, - }); - if (res) { - navigate(Routes.MINT_SHEET, { - collection: res.getSingleCollection?.collection, - }); - } + navigateToMintCollection(contractAddress, network); } }; checkAndHandleMint(searchQuery); diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 3baf73d75b5..e64ea63ed09 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -1,5 +1,4 @@ import { BlurView } from '@react-native-community/blur'; - import React, { useCallback, useEffect, @@ -8,7 +7,7 @@ import React, { useRef, useState, } from 'react'; -import { View } from 'react-native'; +import { Linking, View } from 'react-native'; import { useSharedValue } from 'react-native-reanimated'; import useWallets from '../../hooks/useWallets'; import { GasSpeedButton } from '@/components/gas'; @@ -21,6 +20,7 @@ import Routes from '@/navigation/routesNames'; import ImgixImage from '../../components/images/ImgixImage'; import { SlackSheet } from '../../components/sheet'; import { CardSize } from '../../components/unique-token/CardSize'; +import { WrappedAlert as Alert } from '@/helpers/alert'; import { Box, ColorModeProvider, @@ -73,6 +73,7 @@ import { RainbowError, logger } from '@/logger'; import { useDispatch } from 'react-redux'; import { QuantityButton } from './components/QuantityButton'; import { estimateGas, getProviderForNetwork } from '@/handlers/web3'; +import { Alert } from '@/components/alerts'; const NFT_IMAGE_HEIGHT = 250; @@ -110,8 +111,6 @@ interface MintSheetProps { chainId: number; } -type PoapClaimStatus = 'none' | 'claiming' | 'claimed' | 'error'; - function MintInfoRow({ symbol, label, @@ -162,7 +161,6 @@ const MintSheet = () => { const maxMintsPerWallet = mintCollection.mintStages?.find(item => item?.stage === 'public-sale') ?.maxMintsPerWallet || 3; - console.log(maxMintsPerWallet); const [quantity, setQuantity] = useReducer( (quantity: number, increment: number) => { @@ -206,7 +204,18 @@ const MintSheet = () => { const price = mintCollection.mintStages?.find( item => item?.stage === 'public-sale' )?.price?.amount?.raw; - const isMintingAvailable = mintCollection.isMintingPublicSale; + /* its basically if we we have timestamps we can check a pattern, + if they are both null, then if theres a price we are vibing and can mint forever + +*/ + const isMintingAvailable = + mintCollection.isMintingPublicSale || + mintCollection.mintStages?.find(item => item?.stage === 'public-sale') + ?.price; + console.log( + mintCollection.mintStages?.find(item => item?.stage === 'public-sale') + ); + console.log(mintCollection.mintStages); const imageColor = usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; @@ -246,6 +255,7 @@ const MintSheet = () => { useEffect(() => { const estimateMintGas = async () => { + // should disabled this and see if the broken mints will give us a limit updateTxFee( ethUnits.basic_swap, null, @@ -328,6 +338,7 @@ const MintSheet = () => { 6 )} 􀄯`; + // case where mint isnt eth? prob not with our current entrypoints const mintPriceDisplay = convertRawAmountToBalance( multiply(price || '0', quantity), { @@ -335,6 +346,33 @@ const MintSheet = () => { symbol: 'ETH', } ); + const buildMintDotFunUrl = (contract: string, network: Network) => { + const MintDotFunNetworks = [ + Network.mainnet, + Network.optimism, + Network.base, + Network.zora, + ]; + if (!MintDotFunNetworks.includes(network)) { + // show alert mint.fun does not support + // i18n + Alert.alert('Mint.fun does not support this network'); + } + + let chainSlug = 'ethereum'; + switch (network) { + case Network.optimism: + chainSlug = 'op'; + break; + case Network.base: + chainSlug = 'base'; + break; + case Network.zora: + chainSlug = 'zora'; + break; + } + return `https://mint.fun/${chainSlug}/${contract}`; + }; const actionOnPress = useCallback(async () => { if (isReadOnlyWallet) { @@ -342,6 +380,11 @@ const MintSheet = () => { return; } + // link to mint.fun if reserviornot supporting + if (!isMintingAvailable) { + Linking.openURL(buildMintDotFunUrl(mintCollection.id!, currentNetwork)); + } + logger.info('Minting NFT', { name: mintCollection.name }); analyticsV2.track(event.nftMintsMintingNFT, { collectionName: mintCollection.name || '', @@ -373,6 +416,7 @@ const MintSheet = () => { return; } step.items?.forEach(item => { + console.log('reservior item :', item); if ( item.txHash && !txsRef.current.includes(item.txHash) && @@ -402,7 +446,7 @@ const MintSheet = () => { txsRef.current.push(tx.hash); - // @ts-ignore TODO: fix when we overhaul tx list, types are not good + // @ts-expect-error TODO: fix when we overhaul tx list, types are not good dispatch(dataAddNewTransaction(tx)); analyticsV2.track(event.nftMintsMintedNFT, { collectionName: mintCollection.name || '', @@ -423,6 +467,7 @@ const MintSheet = () => { dispatch, imageColor, imageUrl, + isMintingAvailable, isReadOnlyWallet, mintCollection.id, mintCollection.name, @@ -433,7 +478,7 @@ const MintSheet = () => { const buttonLabel = useMemo(() => { if (!isMintingAvailable) { - return i18n.t(i18n.l.mints.mint_unavailable); + return i18n.t(i18n.l.minting.mint_on_mintdotfun); } if (insufficientEth) { @@ -441,14 +486,14 @@ const MintSheet = () => { } if (mintStatus === 'minting') { - return i18n.t(i18n.l.mints.minting); + return i18n.t(i18n.l.minting.minting); } else if (mintStatus === 'minted') { - return i18n.t(i18n.l.mints.minted); + return i18n.t(i18n.l.minting.minted); } else if (mintStatus === 'error') { - return i18n.t(i18n.l.mints.error_minting); + return i18n.t(i18n.l.minting.error_minting); } - return i18n.t(i18n.l.mints.hold_to_mint); + return i18n.t(i18n.l.minting.hold_to_mint); }, [insufficientEth, isMintingAvailable, mintStatus]); return ( @@ -516,7 +561,7 @@ const MintSheet = () => { - {`${i18n.t(i18n.l.mints.by)} `} + {`${i18n.t(i18n.l.minting.by)} `} {ensAvatar?.imageUrl ? ( @@ -539,7 +584,7 @@ const MintSheet = () => { {` ${ ensName || deployerDisplay || - i18n.t(i18n.l.mints.unknown) + i18n.t(i18n.l.minting.unknown) }`} @@ -560,7 +605,7 @@ const MintSheet = () => { size="13pt" weight="medium" > - {i18n.t(i18n.l.mints.mint_price)} + {i18n.t(i18n.l.minting.mint_price)} { weight="bold" > {mintPriceDisplay.amount === '0' - ? i18n.t(i18n.l.mints.free) + ? i18n.t(i18n.l.minting.free) : mintPriceDisplay.display} @@ -582,6 +627,8 @@ const MintSheet = () => { plusAction={() => setQuantity(1)} minusAction={() => setQuantity(-1)} buttonColor={imageColor} + // i may be being fumb here, need to check infinity mints + maxValue={Number(maxMintsPerWallet)} /> @@ -589,9 +636,7 @@ const MintSheet = () => { {/* @ts-ignore */} { {mintCollection.description && ( - {i18n.t(i18n.l.mints.description)} + {i18n.t(i18n.l.minting.description)} { {mintCollection?.tokenCount && ( { {mintCollection?.createdAt && ( { {mintCollection?.id && ( @@ -697,7 +742,7 @@ const MintSheet = () => { void; shouldLongPressHoldPress: boolean; buttonColor: string; + disabled?: boolean; + threshold?: number; + value: number; }; const StepButton = ({ type, @@ -35,7 +38,13 @@ const StepButton = ({ onPress, shouldLongPressHoldPress, buttonColor, + disabled = false, + threshold, + value, }: StepButtonProps) => { + // should prob change the color here maybe :thinky: + const atThreshold = type === 'plus' ? value === threshold : value === 0; + return ( void; minusAction: () => void; buttonColor: string; + disabled?: boolean; + maxValue: number; }; export function QuantityButton({ value, plusAction, minusAction, buttonColor, + disabled = false, + maxValue, }: StepButtonInputProps) { const longPressHandle = useRef(null); const [trigger, setTrigger] = useState(false); @@ -152,6 +166,8 @@ export function QuantityButton({ onPress={onMinusPress} shouldLongPressHoldPress type={MINUS_ACTION_TYPE} + disabled={disabled} + value={value} /> From 88f6a75d06cff7192d2f88fa2a334b0c5962e9f6 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:43:10 -0700 Subject: [PATCH 16/44] bump reservior sdk --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 13846507df5..48f9d95f226 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "@react-navigation/material-top-tabs": "6.6.2", "@react-navigation/native": "6.1.6", "@react-navigation/stack": "6.3.16", - "@reservoir0x/reservoir-sdk": "1.2.1", + "@reservoir0x/reservoir-sdk": "1.4.5", "@segment/analytics-react-native": "2.15.0", "@segment/sovran-react-native": "1.0.4", "@sentry/react-native": "3.4.1", diff --git a/yarn.lock b/yarn.lock index 5989680534b..80f9b4fe431 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4069,10 +4069,10 @@ color "^4.2.3" warn-once "^0.1.0" -"@reservoir0x/reservoir-sdk@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@reservoir0x/reservoir-sdk/-/reservoir-sdk-1.2.1.tgz#8f1a1330c06c658b98e4fd66d0de994c398197ba" - integrity sha512-+FN4mN5m5zEPAW41ZkO23NBc1ANQA8gIJr7bWE44fOm7T0dMtbBKEHuGsLtwbuKUx+IslbjmdhytkXRfFTPMqQ== +"@reservoir0x/reservoir-sdk@1.4.5": + version "1.4.5" + resolved "https://registry.yarnpkg.com/@reservoir0x/reservoir-sdk/-/reservoir-sdk-1.4.5.tgz#6ff0fd61fea203de4cdde019048fa9d0b0ab08e9" + integrity sha512-Yi4Z0c85fUfPB2mo1FdeCpEgrpN36fDV8gU4RDmsl6IuFYD8pwA/SMtxoo5M0uEN3at+VpVV9utyvB8XlHUFMQ== dependencies: axios "^0.27.2" From 43b4151495bc5b0594b3d6d7f85461af781d4d6e Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:44:06 -0700 Subject: [PATCH 17/44] add fallback color to gas components --- src/components/expanded-state/CustomGasState.js | 10 +++++++--- src/components/gas/GasSpeedButton.js | 9 ++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/expanded-state/CustomGasState.js b/src/components/expanded-state/CustomGasState.js index 9955b63acf7..99ffa34cb93 100644 --- a/src/components/expanded-state/CustomGasState.js +++ b/src/components/expanded-state/CustomGasState.js @@ -10,7 +10,6 @@ import { SlackSheet } from '../sheet'; import { FeesPanel, FeesPanelTabs } from './custom-gas'; import { getTrendKey } from '@/helpers/gas'; import { - useAccountSettings, useColorForAsset, useDimensions, useGas, @@ -42,12 +41,17 @@ const FeesPanelTabswrapper = styled(Column)(margin.object(19, 0, 24, 0)); export default function CustomGasState({ asset }) { const { setParams } = useNavigation(); const { - params: { longFormHeight, speeds, openCustomOptions } = {}, + params: { longFormHeight, speeds, openCustomOptions, fallbackColor } = {}, } = useRoute(); const { colors } = useTheme(); const { height: deviceHeight } = useDimensions(); const keyboardHeight = useKeyboardHeight(); - const colorForAsset = useColorForAsset(asset || {}, null, false, true); + const colorForAsset = useColorForAsset( + asset || {}, + fallbackColor, + false, + true + ); const { selectedGasFee, currentBlockParams, txNetwork } = useGas(); const [canGoBack, setCanGoBack] = useState(true); const { tradeDetails } = useSelector(state => state.swap); diff --git a/src/components/gas/GasSpeedButton.js b/src/components/gas/GasSpeedButton.js index e1403b9efa0..4bff48b0017 100644 --- a/src/components/gas/GasSpeedButton.js +++ b/src/components/gas/GasSpeedButton.js @@ -137,6 +137,7 @@ const GasSpeedButton = ({ asset, currentNetwork, horizontalPadding = 19, + fallbackColor, marginBottom = 20, marginTop = 18, speeds = null, @@ -151,7 +152,12 @@ const GasSpeedButton = ({ const { colors } = useTheme(); const { navigate, goBack } = useNavigation(); const { nativeCurrencySymbol, nativeCurrency } = useAccountSettings(); - const rawColorForAsset = useColorForAsset(asset || {}, null, false, true); + const rawColorForAsset = useColorForAsset( + asset || {}, + fallbackColor, + false, + true + ); const [isLongWait, setIsLongWait] = useState(false); const { inputCurrency, outputCurrency } = useSwapCurrencies(); @@ -229,6 +235,7 @@ const GasSpeedButton = ({ if (gasIsNotReady) return; navigate(Routes.CUSTOM_GAS_SHEET, { asset, + fallbackColor, flashbotTransaction, focusTo: shouldOpenCustomGasSheet.focusTo, openCustomOptions: focusTo => openCustomOptionsRef.current(focusTo), From 0d1c0895ccaf4432c8eda9a8bb14f6911cf63a78 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:45:01 -0700 Subject: [PATCH 18/44] quantityButton: handle disabled state --- src/screens/mints/components/QuantityButton.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/screens/mints/components/QuantityButton.tsx b/src/screens/mints/components/QuantityButton.tsx index 17075b384eb..d066cd45e73 100644 --- a/src/screens/mints/components/QuantityButton.tsx +++ b/src/screens/mints/components/QuantityButton.tsx @@ -6,7 +6,7 @@ import styled from '@/styled-thing'; import { ButtonPressAnimation } from '@/components/animations'; import Row from '@/components/layout/Row'; import { Box, Inline, Text } from '@/design-system'; -import { colors } from '@/styles'; +import { useTheme } from '@/theme'; const PLUS_ACTION_TYPE = 'plus'; const MINUS_ACTION_TYPE = 'minus'; @@ -42,8 +42,10 @@ const StepButton = ({ threshold, value, }: StepButtonProps) => { + const { colors } = useTheme(); // should prob change the color here maybe :thinky: - const atThreshold = type === 'plus' ? value === threshold : value === 0; + const atThreshold = type === 'plus' ? value === threshold : value === 1; + const color = disabled || atThreshold ? colors.grey : buttonColor; return ( @@ -69,7 +71,7 @@ const StepButton = ({ align="center" size="17pt" weight="bold" - color={{ custom: colors.alpha(buttonColor, 0.25) }} + color={{ custom: colors.alpha(color, 0.25) }} > {type === 'plus' ? '􀅼' : '􀅽'} From 8d35ff197c4bd2a8458a43c33a39cc9637da29ff Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:45:31 -0700 Subject: [PATCH 19/44] gas: add util to get total gas price --- src/hooks/useGas.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/hooks/useGas.ts b/src/hooks/useGas.ts index f3e3c200fd9..7d1477a3f6e 100644 --- a/src/hooks/useGas.ts +++ b/src/hooks/useGas.ts @@ -186,6 +186,18 @@ export default function useGas({ [dispatch] ); + const getTotalGasPrice = () => { + const txFee = gasData?.selectedGasFee?.gasFee; + const isLegacyGasNetwork = + getNetworkObj(gasData?.txNetwork).gas.gasType === 'legacy'; + const txFeeValue = isLegacyGasNetwork + ? (txFee as LegacyGasFee)?.estimatedFee + : (txFee as GasFee)?.maxFee; + + const txFeeAmount = fromWei(txFeeValue?.value?.amount); + return txFeeAmount; + }; + return { isGasReady, isSufficientGas, @@ -197,6 +209,7 @@ export default function useGas({ updateGasFeeOption, updateToCustomGasFee, updateTxFee, + getTotalGasPrice, ...gasData, }; } From fca66f262bf57d0005d944504c0300895b26ed10 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:47:26 -0700 Subject: [PATCH 20/44] tweak nft image size for tx cell --- src/resources/nfts/simplehash/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/nfts/simplehash/utils.ts b/src/resources/nfts/simplehash/utils.ts index 4f1070cfa26..7e577893710 100644 --- a/src/resources/nfts/simplehash/utils.ts +++ b/src/resources/nfts/simplehash/utils.ts @@ -435,7 +435,7 @@ export function simpleHashNFTToInternalNFT(nft: ValidatedSimpleHashNFT): NFT { externalUrl: collection.external_url ?? undefined, floorPrices, imageUrl: collection.image_url - ? maybeSignUri(collection.image_url) + ? maybeSignUri(collection.image_url, { w: 100 }) : undefined, name: uniqueTokenType === uniqueTokenTypes.ENS From b3f9396edc72582cc2339ee4db80297e4311a3c5 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:49:40 -0700 Subject: [PATCH 21/44] mint sheet updates --- src/screens/mints/MintSheet.tsx | 154 ++++++++++++++------------------ 1 file changed, 66 insertions(+), 88 deletions(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index e64ea63ed09..bb9432437b5 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -50,32 +50,32 @@ import { ButtonPressAnimation } from '@/components/animations'; import { useFocusEffect, useRoute } from '@react-navigation/native'; import { ReservoirCollection } from '@/graphql/__generated__/arcDev'; import { format } from 'date-fns'; -import { useLegacyNFTs } from '@/resources/nfts'; -import { - ParsedAddressAsset, - TransactionStatus, - TransactionType, -} from '@/entities'; +import { TransactionStatus, TransactionType } from '@/entities'; import * as i18n from '@/languages'; import { analyticsV2 } from '@/analytics'; import { event } from '@/analytics/event'; -import { ETH_ADDRESS, ETH_SYMBOL, ethUnits } from '@/references'; +import { ETH_ADDRESS, ETH_SYMBOL } from '@/references'; import { RainbowNetworks, getNetworkObj } from '@/networks'; import { Network } from '@/networks/types'; import { fetchReverseRecord } from '@/handlers/ens'; import { ContactAvatar } from '@/components/contacts'; - import { addressHashedColorIndex } from '@/utils/profileUtils'; import { loadPrivateKey } from '@/model/wallet'; import { ChainBadge } from '@/components/coin-icon'; -import { convertRawAmountToBalance, multiply } from '@/helpers/utilities'; +import { + add, + convertRawAmountToBalance, + greaterThanOrEqualTo, + multiply, +} from '@/helpers/utilities'; import { RainbowError, logger } from '@/logger'; import { useDispatch } from 'react-redux'; import { QuantityButton } from './components/QuantityButton'; import { estimateGas, getProviderForNetwork } from '@/handlers/web3'; -import { Alert } from '@/components/alerts'; const NFT_IMAGE_HEIGHT = 250; +// inset * 2 -> 28 *2 +const INSET_OFFSET = 56; const BackgroundBlur = styled(BlurView).attrs({ blurAmount: 100, @@ -142,25 +142,37 @@ function MintInfoRow({ ); } +const getFormattedDate = (date: string) => { + return format(new Date(date), 'MMMM dd, yyyy'); +}; + const MintSheet = () => { const params = useRoute(); const { collection: mintCollection } = params.params as MintSheetProps; const { accountAddress } = useAccountProfile(); const { height: deviceHeight, width: deviceWidth } = useDimensions(); - const { navigate, goBack } = useNavigation(); + const { navigate } = useNavigation(); const dispatch = useDispatch(); const { colors, isDarkMode } = useTheme(); const { isReadOnlyWallet } = useWallets(); - + const [insufficientEth, setInsufficientEth] = useState(false); const currentNetwork = RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || Network.mainnet; + const [ensName, setENSName] = useState(''); + const [mintStatus, setMintStatus] = useState< + 'none' | 'minting' | 'minted' | 'error' + >('none'); - const txsRef = useRef([]); + const { data: ensAvatar } = useENSAvatar(ensName, { + enabled: Boolean(ensName), + }); + // if there is no max mint we fallback to JS max int const maxMintsPerWallet = - mintCollection.mintStages?.find(item => item?.stage === 'public-sale') - ?.maxMintsPerWallet || 3; + mintCollection.publicMintInfo?.maxMintsPerWallet || Number.MAX_SAFE_INTEGER; + + const price = mintCollection.publicMintInfo?.price?.amount?.raw; const [quantity, setQuantity] = useReducer( (quantity: number, increment: number) => { @@ -174,7 +186,6 @@ const MintSheet = () => { ) { return quantity; } - return quantity + increment; }, 1 @@ -186,36 +197,14 @@ const MintSheet = () => { stopPollingGasFees, isSufficientGas, isValidGas, + getTotalGasPrice, } = useGas(); - const insufficientEth = isSufficientGas === false && isValidGas; - - const { - data: { nfts }, - } = useLegacyNFTs({ - address: accountAddress, - }); const imageUrl = maybeSignUri(mintCollection.image || ''); const { result: aspectRatio } = usePersistentAspectRatio(imageUrl || ''); - const getFormattedDate = (date: string) => { - return format(new Date(date), 'MMMM dd, yyyy'); - }; - const price = mintCollection.mintStages?.find( - item => item?.stage === 'public-sale' - )?.price?.amount?.raw; - /* its basically if we we have timestamps we can check a pattern, - if they are both null, then if theres a price we are vibing and can mint forever - -*/ - const isMintingAvailable = - mintCollection.isMintingPublicSale || - mintCollection.mintStages?.find(item => item?.stage === 'public-sale') - ?.price; - console.log( - mintCollection.mintStages?.find(item => item?.stage === 'public-sale') - ); - console.log(mintCollection.mintStages); + // isMintingPublicSale handles if theres a time based mint, otherwise if there is a price we should be able to mint + const isMintingAvailable = mintCollection.isMintingPublicSale || price; const imageColor = usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; @@ -233,6 +222,28 @@ const MintSheet = () => { } }); + // check address balance + useEffect(() => { + const checkInsufficientEth = async () => { + const nativeBalance = + ( + await ethereumUtils.getNativeAssetForNetwork( + currentNetwork, + accountAddress + ) + )?.balance?.amount ?? 0; + const txFee = getTotalGasPrice(); + const totalMintPrice = multiply(price || '0', quantity); + // gas price + mint price + // TODO: need to double check this when there are paid mints available + setInsufficientEth( + greaterThanOrEqualTo(add(txFee, totalMintPrice), nativeBalance) + ); + }; + checkInsufficientEth(); + }, [accountAddress, currentNetwork, getTotalGasPrice, price, quantity]); + + // resolve ens name useEffect(() => { const fetchENSName = async (address: string) => { const ensName = await fetchReverseRecord(address); @@ -244,7 +255,7 @@ const MintSheet = () => { } }, [mintCollection.creator]); - // estimate gas + // start poll gas price useEffect(() => { startPollingGasFees(currentNetwork); @@ -253,15 +264,9 @@ const MintSheet = () => { }; }, [currentNetwork, startPollingGasFees, stopPollingGasFees]); + // estimate gas limit useEffect(() => { const estimateMintGas = async () => { - // should disabled this and see if the broken mints will give us a limit - updateTxFee( - ethUnits.basic_swap, - null, - ethUnits.default_l1_gas_fee_optimism_swap - ); - const networkObj = getNetworkObj(currentNetwork); const provider = await getProviderForNetwork(currentNetwork); const signer = createWalletClient({ @@ -276,17 +281,15 @@ const MintSheet = () => { chainId: networkObj.id, precheck: true, onProgress: async (steps: Execute['steps']) => { + console.log(steps); steps.forEach(step => { if (step.error) { logger.error( new RainbowError(`NFT Mints: Gas Step Error: ${step.error}`) ); - // analyticsV2.track(analyticsV2.event.nftOffersAcceptedOffer, { - // status: 'failed', - // ...analyticsEventObject, - // }); return; } + console.log({ price, quantity }); step.items?.forEach(async item => { // could add safety here if unable to calc gas limit const tx = { @@ -295,18 +298,16 @@ const MintSheet = () => { data: item.data?.data, value: multiply(price || '0', quantity), }; + const gas = await estimateGas(tx, provider); if (gas) { updateTxFee(gas, null); - } else { - console.log('ERROR CALCULATING GAS'); } }); }); }, }); }; - estimateMintGas(); }, [ accountAddress, @@ -317,15 +318,6 @@ const MintSheet = () => { updateTxFee, ]); - const [ensName, setENSName] = useState(''); - const [mintStatus, setMintStatus] = useState< - 'none' | 'minting' | 'minted' | 'error' - >('none'); - - const { data: ensAvatar } = useENSAvatar(ensName, { - enabled: Boolean(ensName), - }); - const deployerDisplay = abbreviations.address( mintCollection.creator || '', 4, @@ -356,6 +348,7 @@ const MintSheet = () => { if (!MintDotFunNetworks.includes(network)) { // show alert mint.fun does not support // i18n + // this isnt possible with our current entry points Alert.alert('Mint.fun does not support this network'); } @@ -380,9 +373,10 @@ const MintSheet = () => { return; } - // link to mint.fun if reserviornot supporting + // link to mint.fun if reservoir not supporting if (!isMintingAvailable) { Linking.openURL(buildMintDotFunUrl(mintCollection.id!, currentNetwork)); + return; } logger.info('Minting NFT', { name: mintCollection.name }); @@ -416,12 +410,7 @@ const MintSheet = () => { return; } step.items?.forEach(item => { - console.log('reservior item :', item); - if ( - item.txHash && - !txsRef.current.includes(item.txHash) && - item.status === 'incomplete' - ) { + if (item.txHash && item.status === 'incomplete') { const tx = { to: item.data?.to, from: item.data?.from, @@ -444,8 +433,6 @@ const MintSheet = () => { status: TransactionStatus.minting, }; - txsRef.current.push(tx.hash); - // @ts-expect-error TODO: fix when we overhaul tx list, types are not good dispatch(dataAddNewTransaction(tx)); analyticsV2.track(event.nftMintsMintedNFT, { @@ -480,7 +467,6 @@ const MintSheet = () => { if (!isMintingAvailable) { return i18n.t(i18n.l.minting.mint_on_mintdotfun); } - if (insufficientEth) { return i18n.t(i18n.l.button.confirm_exchange.insufficient_eth); } @@ -592,7 +578,7 @@ const MintSheet = () => { - + @@ -607,7 +593,6 @@ const MintSheet = () => { > {i18n.t(i18n.l.minting.mint_price)} - { plusAction={() => setQuantity(1)} minusAction={() => setQuantity(-1)} buttonColor={imageColor} - // i may be being fumb here, need to check infinity mints + disabled={!isMintingAvailable} maxValue={Number(maxMintsPerWallet)} /> @@ -644,12 +629,10 @@ const MintSheet = () => { showBiometryIcon={!insufficientEth} /> - + {/* @ts-ignore */} { /> - - {/* - nPress={()=> ethereumUtils.openAddressInBlockExplorer(mintCollection?.contract, currentNetwork)} value={contractAddressDisplay} color={imageColor} - - */} {mintCollection.description && ( @@ -782,7 +760,7 @@ const MintSheet = () => { - + From d026b652c83e6e921b43b7ebc4dad8b7bac0d374 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:52:50 -0700 Subject: [PATCH 22/44] i18n --- src/languages/en_US.json | 6 +++++- src/resources/reservoir/mints.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/languages/en_US.json b/src/languages/en_US.json index 0128bc01751..49e465c3b9e 100644 --- a/src/languages/en_US.json +++ b/src/languages/en_US.json @@ -1169,7 +1169,9 @@ "last_event": "Last Event", "max_supply": "Max Supply", "contract": "Contract", - "network": "Network" + "network": "Network", + "could_not_find_collection": "Could not find collection", + "unable_to_find_check_again": "We are unable to find this collection, double check the address and network or try again later" }, "nfts": { "selling": "Selling" @@ -1712,6 +1714,8 @@ "failed": "Failed", "purchased": "Purchased", "purchasing": "Purchasing", + "minting": "Minting", + "minted": "Minted", "received": "Received", "receiving": "Receiving", "self": "Self", diff --git a/src/resources/reservoir/mints.ts b/src/resources/reservoir/mints.ts index 0358d07e3a5..3d168f92819 100644 --- a/src/resources/reservoir/mints.ts +++ b/src/resources/reservoir/mints.ts @@ -12,8 +12,8 @@ import * as lang from '@/languages'; const client = IS_DEV ? arcDevClient : arcClient; const showAlert = () => { Alert.alert( - 'Could not find collection', - 'We are unable to find this collection, double check the address and network or try again later', + lang.t(lang.l.minting.could_not_find_collection), + lang.t(lang.l.minting.unable_to_find_check_again), [{ text: lang.t(lang.l.button.ok) }], { cancelable: false } ); From 37b9070d38c267688fea38680f489fb4e048f439 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 22:53:06 -0700 Subject: [PATCH 23/44] query update --- src/graphql/queries/arc.graphql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/graphql/queries/arc.graphql b/src/graphql/queries/arc.graphql index 59860a9415a..c05e9439b19 100644 --- a/src/graphql/queries/arc.graphql +++ b/src/graphql/queries/arc.graphql @@ -115,8 +115,7 @@ query getReservoirCollection($contractAddress: String!, $chainId: Int!) { creator ownerCount isMintingPublicSale - isMintingPresale - mintStages { + publicMintInfo { stage kind price { From 3236ea832f6e2f94943dcc057c424d07f0974aa9 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Sun, 1 Oct 2023 23:00:17 -0700 Subject: [PATCH 24/44] use dev endpoints for testing --- src/graphql/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphql/config.js b/src/graphql/config.js index 69bbea91490..30f220bef11 100644 --- a/src/graphql/config.js +++ b/src/graphql/config.js @@ -17,7 +17,7 @@ exports.config = { document: './queries/arc.graphql', schema: { method: 'GET', - url: 'https://arc-graphql.rainbow.me/graphql', + url: 'https://arc-graphql.rainbowdotme.workers.dev/graphql', headers: { 'x-api-key': 'ARC_GRAPHQL_API_KEY', }, From 3262ddc13d4c8d0a31cc2f4ec1ffb46ece3c0086 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 2 Oct 2023 00:01:13 -0700 Subject: [PATCH 25/44] add back tx ref --- src/screens/mints/MintSheet.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index bb9432437b5..38135496be8 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -163,6 +163,7 @@ const MintSheet = () => { const [mintStatus, setMintStatus] = useState< 'none' | 'minting' | 'minted' | 'error' >('none'); + const txRef = useRef(); const { data: ensAvatar } = useENSAvatar(ensName, { enabled: Boolean(ensName), @@ -410,7 +411,11 @@ const MintSheet = () => { return; } step.items?.forEach(item => { - if (item.txHash && item.status === 'incomplete') { + if ( + item.txHash && + txRef.current !== item.txHash && + item.status === 'incomplete' + ) { const tx = { to: item.data?.to, from: item.data?.from, @@ -433,6 +438,7 @@ const MintSheet = () => { status: TransactionStatus.minting, }; + txRef.current = tx.hash; // @ts-expect-error TODO: fix when we overhaul tx list, types are not good dispatch(dataAddNewTransaction(tx)); analyticsV2.track(event.nftMintsMintedNFT, { From 88f1152b2baecc70ea72cd75db7124a7dcfa346c Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 2 Oct 2023 09:58:05 -0700 Subject: [PATCH 26/44] tweak quantity button disabled color --- src/screens/mints/components/QuantityButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/screens/mints/components/QuantityButton.tsx b/src/screens/mints/components/QuantityButton.tsx index d066cd45e73..c978b0211b5 100644 --- a/src/screens/mints/components/QuantityButton.tsx +++ b/src/screens/mints/components/QuantityButton.tsx @@ -42,10 +42,10 @@ const StepButton = ({ threshold, value, }: StepButtonProps) => { - const { colors } = useTheme(); + const { colors, darkScheme } = useTheme(); // should prob change the color here maybe :thinky: const atThreshold = type === 'plus' ? value === threshold : value === 1; - const color = disabled || atThreshold ? colors.grey : buttonColor; + const color = disabled || atThreshold ? darkScheme.grey : buttonColor; return ( Date: Mon, 2 Oct 2023 18:34:15 -0700 Subject: [PATCH 27/44] fix disabled state color --- src/screens/mints/components/QuantityButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/screens/mints/components/QuantityButton.tsx b/src/screens/mints/components/QuantityButton.tsx index c978b0211b5..68a7413d0af 100644 --- a/src/screens/mints/components/QuantityButton.tsx +++ b/src/screens/mints/components/QuantityButton.tsx @@ -42,10 +42,10 @@ const StepButton = ({ threshold, value, }: StepButtonProps) => { - const { colors, darkScheme } = useTheme(); + const { colors, lightScheme } = useTheme(); // should prob change the color here maybe :thinky: const atThreshold = type === 'plus' ? value === threshold : value === 1; - const color = disabled || atThreshold ? darkScheme.grey : buttonColor; + const color = disabled || atThreshold ? lightScheme.grey : buttonColor; return ( Date: Mon, 2 Oct 2023 18:35:32 -0700 Subject: [PATCH 28/44] add max label --- src/screens/mints/MintSheet.tsx | 63 +++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 38135496be8..36ba083a907 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -154,7 +154,7 @@ const MintSheet = () => { const { navigate } = useNavigation(); const dispatch = useDispatch(); const { colors, isDarkMode } = useTheme(); - const { isReadOnlyWallet } = useWallets(); + const { isReadOnlyWallet, isHardwareWallet } = useWallets(); const [insufficientEth, setInsufficientEth] = useState(false); const currentNetwork = RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || @@ -205,7 +205,9 @@ const MintSheet = () => { const { result: aspectRatio } = usePersistentAspectRatio(imageUrl || ''); // isMintingPublicSale handles if theres a time based mint, otherwise if there is a price we should be able to mint - const isMintingAvailable = mintCollection.isMintingPublicSale || price; + const isMintingAvailable = + !(isReadOnlyWallet || isHardwareWallet) && + (mintCollection.isMintingPublicSale || price); const imageColor = usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; @@ -590,37 +592,54 @@ const MintSheet = () => { - + {i18n.t(i18n.l.minting.mint_price)} - - {mintPriceDisplay.amount === '0' - ? i18n.t(i18n.l.minting.free) - : mintPriceDisplay.display} - + + + {mintPriceDisplay.amount === '0' + ? i18n.t(i18n.l.minting.free) + : mintPriceDisplay.display} + + - setQuantity(1)} - minusAction={() => setQuantity(-1)} - buttonColor={imageColor} - disabled={!isMintingAvailable} - maxValue={Number(maxMintsPerWallet)} - /> + + { + + {quantity === Number(maxMintsPerWallet) + ? 'Max' + : ''} + + } + + setQuantity(1)} + minusAction={() => setQuantity(-1)} + buttonColor={imageColor} + disabled={!isMintingAvailable} + maxValue={Number(maxMintsPerWallet)} + /> + From 1b2246ff7c72246c073938f3e38cce84770de250 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 2 Oct 2023 18:45:49 -0700 Subject: [PATCH 29/44] fix price calculation --- src/screens/mints/MintSheet.tsx | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 36ba083a907..b0b0acef256 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -173,7 +173,13 @@ const MintSheet = () => { const maxMintsPerWallet = mintCollection.publicMintInfo?.maxMintsPerWallet || Number.MAX_SAFE_INTEGER; - const price = mintCollection.publicMintInfo?.price?.amount?.raw; + const price = convertRawAmountToBalance( + mintCollection.publicMintInfo?.price?.amount?.raw || '0', + { + decimals: mintCollection.publicMintInfo?.price?.currency?.decimals || 18, + symbol: mintCollection.publicMintInfo?.price?.currency?.symbol || 'ETH', + } + ); const [quantity, setQuantity] = useReducer( (quantity: number, increment: number) => { @@ -236,7 +242,7 @@ const MintSheet = () => { ) )?.balance?.amount ?? 0; const txFee = getTotalGasPrice(); - const totalMintPrice = multiply(price || '0', quantity); + const totalMintPrice = multiply(price.amount, quantity); // gas price + mint price // TODO: need to double check this when there are paid mints available setInsufficientEth( @@ -244,7 +250,15 @@ const MintSheet = () => { ); }; checkInsufficientEth(); - }, [accountAddress, currentNetwork, getTotalGasPrice, price, quantity]); + }, [ + accountAddress, + currentNetwork, + getTotalGasPrice, + mintCollection.publicMintInfo?.price?.currency?.decimals, + mintCollection.publicMintInfo?.price?.currency?.symbol, + price, + quantity, + ]); // resolve ens name useEffect(() => { @@ -299,7 +313,7 @@ const MintSheet = () => { to: item.data?.to, from: item.data?.from, data: item.data?.data, - value: multiply(price || '0', quantity), + value: multiply(price.amount || '0', quantity), }; const gas = await estimateGas(tx, provider); @@ -335,7 +349,7 @@ const MintSheet = () => { // case where mint isnt eth? prob not with our current entrypoints const mintPriceDisplay = convertRawAmountToBalance( - multiply(price || '0', quantity), + multiply(price.amount, quantity), { decimals: 18, symbol: 'ETH', From 3a059548a63468c9570766777f542e124fbfa4f6 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 2 Oct 2023 20:33:16 -0700 Subject: [PATCH 30/44] max mint fallback + native display --- src/screens/mints/MintSheet.tsx | 93 ++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index b0b0acef256..f3d90894f55 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -34,6 +34,7 @@ import { } from '@/design-system'; import { useAccountProfile, + useAccountSettings, useDimensions, useENSAvatar, useGas, @@ -64,8 +65,11 @@ import { loadPrivateKey } from '@/model/wallet'; import { ChainBadge } from '@/components/coin-icon'; import { add, + convertAmountToBalanceDisplay, + convertAmountToNativeDisplay, convertRawAmountToBalance, greaterThanOrEqualTo, + isZero, multiply, } from '@/helpers/utilities'; import { RainbowError, logger } from '@/logger'; @@ -150,12 +154,14 @@ const MintSheet = () => { const params = useRoute(); const { collection: mintCollection } = params.params as MintSheetProps; const { accountAddress } = useAccountProfile(); + const { nativeCurrency } = useAccountSettings(); const { height: deviceHeight, width: deviceWidth } = useDimensions(); const { navigate } = useNavigation(); const dispatch = useDispatch(); const { colors, isDarkMode } = useTheme(); const { isReadOnlyWallet, isHardwareWallet } = useWallets(); const [insufficientEth, setInsufficientEth] = useState(false); + const [showNativePrice, setShowNativePrice] = useState(false); const currentNetwork = RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || Network.mainnet; @@ -169,18 +175,6 @@ const MintSheet = () => { enabled: Boolean(ensName), }); - // if there is no max mint we fallback to JS max int - const maxMintsPerWallet = - mintCollection.publicMintInfo?.maxMintsPerWallet || Number.MAX_SAFE_INTEGER; - - const price = convertRawAmountToBalance( - mintCollection.publicMintInfo?.price?.amount?.raw || '0', - { - decimals: mintCollection.publicMintInfo?.price?.currency?.decimals || 18, - symbol: mintCollection.publicMintInfo?.price?.currency?.symbol || 'ETH', - } - ); - const [quantity, setQuantity] = useReducer( (quantity: number, increment: number) => { if (quantity === 1 && increment === -1) { @@ -198,6 +192,36 @@ const MintSheet = () => { 1 ); + // if there is no max mint info, we fallback to 1 to be safe + const maxMintsPerWallet = + mintCollection.publicMintInfo?.maxMintsPerWallet || 1; + console.log({ maxMintsPerWallet }); + + const price = convertRawAmountToBalance( + mintCollection.publicMintInfo?.price?.amount?.raw || '0', + { + decimals: mintCollection.publicMintInfo?.price?.currency?.decimals || 18, + symbol: mintCollection.publicMintInfo?.price?.currency?.symbol || 'ETH', + } + ); + + // case where mint isnt eth? prob not with our current entrypoints + const mintPriceAmount = multiply(price.amount, quantity); + const mintPriceDisplay = convertAmountToBalanceDisplay( + multiply(price.amount, quantity), + { + decimals: mintCollection.publicMintInfo?.price?.currency?.decimals || 18, + symbol: mintCollection.publicMintInfo?.price?.currency?.symbol || 'ETH', + } + ); + + const priceOfEth = ethereumUtils.getEthPriceUnit() as number; + + const nativeMintPriceDisplay = convertAmountToNativeDisplay( + parseFloat(multiply(price.amount, quantity)) * priceOfEth, + nativeCurrency + ); + const { updateTxFee, startPollingGasFees, @@ -298,7 +322,6 @@ const MintSheet = () => { chainId: networkObj.id, precheck: true, onProgress: async (steps: Execute['steps']) => { - console.log(steps); steps.forEach(step => { if (step.error) { logger.error( @@ -306,7 +329,6 @@ const MintSheet = () => { ); return; } - console.log({ price, quantity }); step.items?.forEach(async item => { // could add safety here if unable to calc gas limit const tx = { @@ -347,14 +369,6 @@ const MintSheet = () => { 6 )} 􀄯`; - // case where mint isnt eth? prob not with our current entrypoints - const mintPriceDisplay = convertRawAmountToBalance( - multiply(price.amount, quantity), - { - decimals: 18, - symbol: 'ETH', - } - ); const buildMintDotFunUrl = (contract: string, network: Network) => { const MintDotFunNetworks = [ Network.mainnet, @@ -437,7 +451,7 @@ const MintSheet = () => { from: item.data?.from, hash: item.txHash, network: currentNetwork, - amount: mintPriceDisplay.amount, + amount: mintPriceAmount, asset: { address: ETH_ADDRESS, symbol: ETH_SYMBOL, @@ -480,7 +494,7 @@ const MintSheet = () => { isReadOnlyWallet, mintCollection.id, mintCollection.name, - mintPriceDisplay.amount, + mintPriceAmount, navigate, quantity, ]); @@ -615,18 +629,25 @@ const MintSheet = () => { > {i18n.t(i18n.l.minting.mint_price)} - - - {mintPriceDisplay.amount === '0' - ? i18n.t(i18n.l.minting.free) - : mintPriceDisplay.display} - - + setShowNativePrice(!showNativePrice)} + > + + + {isZero(mintPriceAmount) + ? i18n.t(i18n.l.minting.free) + : showNativePrice + ? nativeMintPriceDisplay + : mintPriceDisplay} + + + From 294a42a3bcbfcb460e601ed3628600e4eae969f0 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 2 Oct 2023 20:38:54 -0700 Subject: [PATCH 31/44] add refferal addresses --- src/resources/reservoir/utils.ts | 34 +++++++++++++++++++++++ src/screens/NFTSingleOfferSheet/index.tsx | 34 +---------------------- src/screens/mints/MintSheet.tsx | 11 +++++++- 3 files changed, 45 insertions(+), 34 deletions(-) create mode 100644 src/resources/reservoir/utils.ts diff --git a/src/resources/reservoir/utils.ts b/src/resources/reservoir/utils.ts new file mode 100644 index 00000000000..9003a8279d8 --- /dev/null +++ b/src/resources/reservoir/utils.ts @@ -0,0 +1,34 @@ +import { Network } from '@/networks/types'; + +const RAINBOW_FEE_ADDRESS_MAINNET = + '0x69d6d375de8c7ade7e44446df97f49e661fdad7d'; +const RAINBOW_FEE_ADDRESS_POLYGON = + '0xfb9af3db5e19c4165f413f53fe3bbe6226834548'; +const RAINBOW_FEE_ADDRESS_OPTIMISM = + '0x0d9b71891dc86400acc7ead08c80af301ccb3d71'; +const RAINBOW_FEE_ADDRESS_ARBITRUM = + '0x0f9259af03052c96afda88add62eb3b5cbc185f1'; +const RAINBOW_FEE_ADDRESS_BASE = '0x1bbe055ad3204fa4468b4e6d3a3c59b9d9ac8c19'; +const RAINBOW_FEE_ADDRESS_BSC = '0x9670271ec2e2937a2e9df536784344bbff2bbea6'; +const RAINBOW_FEE_ADDRESS_ZORA = '0x7a3d05c70581bd345fe117c06e45f9669205384f'; + +export function getRainbowFeeAddress(network: Network) { + switch (network) { + case Network.mainnet: + return RAINBOW_FEE_ADDRESS_MAINNET; + case Network.polygon: + return RAINBOW_FEE_ADDRESS_POLYGON; + case Network.optimism: + return RAINBOW_FEE_ADDRESS_OPTIMISM; + case Network.arbitrum: + return RAINBOW_FEE_ADDRESS_ARBITRUM; + case Network.base: + return RAINBOW_FEE_ADDRESS_BASE; + case Network.bsc: + return RAINBOW_FEE_ADDRESS_BSC; + case Network.zora: + return RAINBOW_FEE_ADDRESS_ZORA; + default: + return undefined; + } +} diff --git a/src/screens/NFTSingleOfferSheet/index.tsx b/src/screens/NFTSingleOfferSheet/index.tsx index fcea45e7761..dc30430a888 100644 --- a/src/screens/NFTSingleOfferSheet/index.tsx +++ b/src/screens/NFTSingleOfferSheet/index.tsx @@ -50,45 +50,13 @@ import { getNetworkObj } from '@/networks'; import { CardSize } from '@/components/unique-token/CardSize'; import { queryClient } from '@/react-query'; import { nftOffersQueryKey } from '@/resources/reservoir/nftOffersQuery'; +import { getRainbowFeeAddress } from '@/resources/reservoir/utils'; const NFT_IMAGE_HEIGHT = 160; const TWO_HOURS_MS = 2 * 60 * 60 * 1000; const RAINBOW_FEE_BIPS = 85; const BIPS_TO_DECIMAL_RATIO = 10000; -const RAINBOW_FEE_ADDRESS_MAINNET = - '0x69d6d375de8c7ade7e44446df97f49e661fdad7d'; -const RAINBOW_FEE_ADDRESS_POLYGON = - '0xfb9af3db5e19c4165f413f53fe3bbe6226834548'; -const RAINBOW_FEE_ADDRESS_OPTIMISM = - '0x0d9b71891dc86400acc7ead08c80af301ccb3d71'; -const RAINBOW_FEE_ADDRESS_ARBITRUM = - '0x0f9259af03052c96afda88add62eb3b5cbc185f1'; -const RAINBOW_FEE_ADDRESS_BASE = '0x1bbe055ad3204fa4468b4e6d3a3c59b9d9ac8c19'; -const RAINBOW_FEE_ADDRESS_BSC = '0x9670271ec2e2937a2e9df536784344bbff2bbea6'; -const RAINBOW_FEE_ADDRESS_ZORA = '0x7a3d05c70581bd345fe117c06e45f9669205384f'; - -function getRainbowFeeAddress(network: Network) { - switch (network) { - case Network.mainnet: - return RAINBOW_FEE_ADDRESS_MAINNET; - case Network.polygon: - return RAINBOW_FEE_ADDRESS_POLYGON; - case Network.optimism: - return RAINBOW_FEE_ADDRESS_OPTIMISM; - case Network.arbitrum: - return RAINBOW_FEE_ADDRESS_ARBITRUM; - case Network.base: - return RAINBOW_FEE_ADDRESS_BASE; - case Network.bsc: - return RAINBOW_FEE_ADDRESS_BSC; - case Network.zora: - return RAINBOW_FEE_ADDRESS_ZORA; - default: - return undefined; - } -} - function Row({ symbol, label, diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index f3d90894f55..3dd6b280148 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -76,6 +76,7 @@ import { RainbowError, logger } from '@/logger'; import { useDispatch } from 'react-redux'; import { QuantityButton } from './components/QuantityButton'; import { estimateGas, getProviderForNetwork } from '@/handlers/web3'; +import { getRainbowFeeAddress } from '@/resources/reservoir/utils'; const NFT_IMAGE_HEIGHT = 250; // inset * 2 -> 28 *2 @@ -429,8 +430,16 @@ const MintSheet = () => { transport: http(networkObj.rpc), }); + const feeAddress = getRainbowFeeAddress(currentNetwork); getClient()?.actions.buyToken({ - items: [{ fillType: 'mint', collection: mintCollection.id!, quantity }], + items: [ + { + fillType: 'mint', + collection: mintCollection.id!, + quantity, + ...(feeAddress && { referrer: feeAddress }), + }, + ], wallet: signer!, chainId: networkObj.id, onProgress: (steps: Execute['steps']) => { From b253c49850cea2124b719fc83f1fb936f36e7e8f Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Mon, 2 Oct 2023 20:40:22 -0700 Subject: [PATCH 32/44] rm log --- src/screens/mints/MintSheet.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 3dd6b280148..0a58d5ce237 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -196,7 +196,6 @@ const MintSheet = () => { // if there is no max mint info, we fallback to 1 to be safe const maxMintsPerWallet = mintCollection.publicMintInfo?.maxMintsPerWallet || 1; - console.log({ maxMintsPerWallet }); const price = convertRawAmountToBalance( mintCollection.publicMintInfo?.price?.amount?.raw || '0', From fe4d705277667d210fd6605f94ff09e2256f0658 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 5 Oct 2023 14:13:16 -0400 Subject: [PATCH 33/44] error handling --- src/screens/mints/MintSheet.tsx | 209 ++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 93 deletions(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 0a58d5ce237..3a7433f392e 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -163,6 +163,7 @@ const MintSheet = () => { const { isReadOnlyWallet, isHardwareWallet } = useWallets(); const [insufficientEth, setInsufficientEth] = useState(false); const [showNativePrice, setShowNativePrice] = useState(false); + const [gasError, setGasError] = useState(false); const currentNetwork = RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || Network.mainnet; @@ -194,8 +195,9 @@ const MintSheet = () => { ); // if there is no max mint info, we fallback to 1 to be safe - const maxMintsPerWallet = - mintCollection.publicMintInfo?.maxMintsPerWallet || 1; + const maxMintsPerWallet = Number( + mintCollection.publicMintInfo?.maxMintsPerWallet + ); const price = convertRawAmountToBalance( mintCollection.publicMintInfo?.price?.amount?.raw || '0', @@ -237,7 +239,8 @@ const MintSheet = () => { // isMintingPublicSale handles if theres a time based mint, otherwise if there is a price we should be able to mint const isMintingAvailable = !(isReadOnlyWallet || isHardwareWallet) && - (mintCollection.isMintingPublicSale || price); + (mintCollection.isMintingPublicSale || price) && + !gasError; const imageColor = usePersistentDominantColorFromImage(imageUrl) ?? colors.paleBlue; @@ -315,37 +318,46 @@ const MintSheet = () => { chain: networkObj, transport: http(networkObj.rpc), }); - - getClient()?.actions.buyToken({ - items: [{ fillType: 'mint', collection: mintCollection.id!, quantity }], - wallet: signer!, - chainId: networkObj.id, - precheck: true, - onProgress: async (steps: Execute['steps']) => { - steps.forEach(step => { - if (step.error) { - logger.error( - new RainbowError(`NFT Mints: Gas Step Error: ${step.error}`) - ); - return; - } - step.items?.forEach(async item => { - // could add safety here if unable to calc gas limit - const tx = { - to: item.data?.to, - from: item.data?.from, - data: item.data?.data, - value: multiply(price.amount || '0', quantity), - }; - - const gas = await estimateGas(tx, provider); - if (gas) { - updateTxFee(gas, null); + try { + await getClient()?.actions.buyToken({ + items: [ + { fillType: 'mint', collection: mintCollection.id!, quantity }, + ], + wallet: signer!, + chainId: networkObj.id, + precheck: true, + onProgress: async (steps: Execute['steps']) => { + steps.forEach(step => { + if (step.error) { + logger.error( + new RainbowError(`NFT Mints: Gas Step Error: ${step.error}`) + ); + return; } + step.items?.forEach(async item => { + // could add safety here if unable to calc gas limit + const tx = { + to: item.data?.to, + from: item.data?.from, + data: item.data?.data, + value: multiply(price.amount || '0', quantity), + }; + + const gas = await estimateGas(tx, provider); + if (gas) { + setGasError(false); + updateTxFee(gas, null); + } + }); }); - }); - }, - }); + }, + }); + } catch (e) { + setGasError(true); + logger.error( + new RainbowError(`NFT Mints: Gas Step Error: ${(e as Error).message}`) + ); + } }; estimateMintGas(); }, [ @@ -430,68 +442,77 @@ const MintSheet = () => { }); const feeAddress = getRainbowFeeAddress(currentNetwork); - getClient()?.actions.buyToken({ - items: [ - { - fillType: 'mint', - collection: mintCollection.id!, - quantity, - ...(feeAddress && { referrer: feeAddress }), - }, - ], - wallet: signer!, - chainId: networkObj.id, - onProgress: (steps: Execute['steps']) => { - steps.forEach(step => { - if (step.error) { - logger.error(new RainbowError(`Error minting NFT: ${step.error}`)); - setMintStatus('error'); - return; - } - step.items?.forEach(item => { - if ( - item.txHash && - txRef.current !== item.txHash && - item.status === 'incomplete' - ) { - const tx = { - to: item.data?.to, - from: item.data?.from, - hash: item.txHash, - network: currentNetwork, - amount: mintPriceAmount, - asset: { - address: ETH_ADDRESS, - symbol: ETH_SYMBOL, - }, - nft: { - predominantColor: imageColor, - collection: { - image: imageUrl, - }, - lowResUrl: imageUrl, - name: mintCollection.name, - }, - type: TransactionType.mint, - status: TransactionStatus.minting, - }; - - txRef.current = tx.hash; - // @ts-expect-error TODO: fix when we overhaul tx list, types are not good - dispatch(dataAddNewTransaction(tx)); - analyticsV2.track(event.nftMintsMintedNFT, { - collectionName: mintCollection.name || '', - contract: mintCollection.id || '', - network: currentNetwork, - quantity, - }); - navigate(Routes.PROFILE_SCREEN); - setMintStatus('minted'); + try { + await getClient()?.actions.buyToken({ + items: [ + { + fillType: 'mint', + collection: mintCollection.id!, + quantity, + ...(feeAddress && { referrer: feeAddress }), + }, + ], + wallet: signer!, + chainId: networkObj.id, + onProgress: (steps: Execute['steps']) => { + steps.forEach(step => { + if (step.error) { + logger.error( + new RainbowError(`Error minting NFT: ${step.error}`) + ); + setMintStatus('error'); + return; } + step.items?.forEach(item => { + if ( + item.txHash && + txRef.current !== item.txHash && + item.status === 'incomplete' + ) { + const tx = { + to: item.data?.to, + from: item.data?.from, + hash: item.txHash, + network: currentNetwork, + amount: mintPriceAmount, + asset: { + address: ETH_ADDRESS, + symbol: ETH_SYMBOL, + }, + nft: { + predominantColor: imageColor, + collection: { + image: imageUrl, + }, + lowResUrl: imageUrl, + name: mintCollection.name, + }, + type: TransactionType.mint, + status: TransactionStatus.minting, + }; + + txRef.current = tx.hash; + // @ts-expect-error TODO: fix when we overhaul tx list, types are not good + dispatch(dataAddNewTransaction(tx)); + analyticsV2.track(event.nftMintsMintedNFT, { + collectionName: mintCollection.name || '', + contract: mintCollection.id || '', + network: currentNetwork, + quantity, + }); + navigate(Routes.PROFILE_SCREEN); + setMintStatus('minted'); + } + }); }); - }); - }, - }); + }, + }); + } catch (e) { + setMintStatus('error'); + logger.error( + new RainbowError(`Error minting NFT: ${(e as Error).message}`) + ); + } }, [ accountAddress, currentNetwork, @@ -688,7 +709,9 @@ const MintSheet = () => { {/* @ts-ignore */} Date: Thu, 12 Oct 2023 10:37:03 -0400 Subject: [PATCH 34/44] useCallbacks --- src/components/cards/FeaturedMintCard.tsx | 44 +++++++++---------- .../cards/MintsCard/CollectionCell.tsx | 29 +++++++----- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/components/cards/FeaturedMintCard.tsx b/src/components/cards/FeaturedMintCard.tsx index 73e290d9f60..402904ded56 100644 --- a/src/components/cards/FeaturedMintCard.tsx +++ b/src/components/cards/FeaturedMintCard.tsx @@ -13,7 +13,7 @@ import { useColorMode, useForegroundColor, } from '@/design-system'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { ButtonPressAnimation } from '../animations'; import { useMints } from '@/resources/mints'; import { useAccountProfile, useDimensions } from '@/hooks'; @@ -24,7 +24,7 @@ import { convertRawAmountToRoundedDecimal, } from '@/helpers/utilities'; import { BlurView } from '@react-native-community/blur'; -import { Linking, View } from 'react-native'; +import { View } from 'react-native'; import { IS_IOS } from '@/env'; import { Media } from '../Media'; import { analyticsV2 } from '@/analytics'; @@ -73,6 +73,24 @@ export function FeaturedMintCard() { useEffect(() => setMediaRendered(false), [imageUrl]); + const handlePress = useCallback(() => { + if (featuredMint) { + analyticsV2.track(analyticsV2.event.mintsPressedFeaturedMintCard, { + contractAddress: featuredMint.contractAddress, + chainId: featuredMint.chainId, + totalMints: featuredMint.totalMints, + mintsLastHour: featuredMint.totalMints, + priceInEth: convertRawAmountToRoundedDecimal( + featuredMint.mintStatus.price, + 18, + 6 + ), + }); + const network = ethereumUtils.getNetworkFromChainId(featuredMint.chainId); + navigateToMintCollection(featuredMint.contract, network); + } + }, [featuredMint]); + return featuredMint ? ( @@ -110,27 +128,7 @@ export function FeaturedMintCard() { overflow: 'hidden', padding: 12, }} - onPress={() => { - analyticsV2.track( - analyticsV2.event.mintsPressedFeaturedMintCard, - { - contractAddress: featuredMint.contractAddress, - chainId: featuredMint.chainId, - totalMints: featuredMint.totalMints, - mintsLastHour: featuredMint.totalMints, - priceInEth: convertRawAmountToRoundedDecimal( - featuredMint.mintStatus.price, - 18, - 6 - ), - } - ); - const network = ethereumUtils.getNetworkFromChainId( - featuredMint.chainId - ); - navigateToMintCollection(featuredMint.contract, network); - // Linking.openURL(featuredMint.externalURL); - }} + onPress={handlePress} scaleTo={0.96} > diff --git a/src/components/cards/MintsCard/CollectionCell.tsx b/src/components/cards/MintsCard/CollectionCell.tsx index bbdf202b1fa..2520166f976 100644 --- a/src/components/cards/MintsCard/CollectionCell.tsx +++ b/src/components/cards/MintsCard/CollectionCell.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { globalColors } from '@/design-system/color/palettes'; import { convertRawAmountToRoundedDecimal } from '@/helpers/utilities'; import { CoinIcon } from '@/components/coin-icon'; @@ -91,18 +91,25 @@ export function CollectionCell({ useEffect(() => setMediaRendered(false), [imageUrl]); + const handlePress = useCallback(() => { + analyticsV2.track(analyticsV2.event.mintsPressedCollectionCell, { + contractAddress: collection.contractAddress, + chainId: collection.chainId, + priceInEth: amount, + }); + + const network = ethereumUtils.getNetworkFromChainId(collection.chainId); + navigateToMintCollection(collection.contract, network); + }, [ + amount, + collection.chainId, + collection.contract, + collection.contractAddress, + ]); + return ( { - analyticsV2.track(analyticsV2.event.mintsPressedCollectionCell, { - contractAddress: collection.contractAddress, - chainId: collection.chainId, - priceInEth: amount, - }); - - const network = ethereumUtils.getNetworkFromChainId(collection.chainId); - navigateToMintCollection(collection.contract, network); - }} + onPress={handlePress} style={{ width: NFT_IMAGE_SIZE }} > Date: Thu, 12 Oct 2023 10:43:40 -0400 Subject: [PATCH 35/44] clean up search logic --- src/screens/discover/components/DiscoverSearch.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/screens/discover/components/DiscoverSearch.js b/src/screens/discover/components/DiscoverSearch.js index 40a29b08751..6c8bd913edb 100644 --- a/src/screens/discover/components/DiscoverSearch.js +++ b/src/screens/discover/components/DiscoverSearch.js @@ -37,8 +37,6 @@ import { getPoapAndOpenSheetWithQRHash, getPoapAndOpenSheetWithSecretWord, } from '@/utils/poaps'; -import { arcDevClient } from '@/graphql'; -import { getNetworkObj } from '@/networks'; import { navigateToMintCollection } from '@/resources/reservoir/mints'; export const SearchContainer = styled(Row)({ @@ -155,12 +153,12 @@ export default function DiscoverSearch() { const checkAndHandleMint = async seachQueryForMint => { if (seachQueryForMint.includes('mint.fun')) { const mintdotfunURL = seachQueryForMint.split('https://mint.fun/'); - console.log(seachQueryForMint); - console.log(mintdotfunURL); const query = mintdotfunURL[1]; let network = query.split('/')[0]; if (network === 'ethereum') { - network = 'mainnet'; + network = Network.mainnet; + } else if (network === 'op') { + network === Network.optimism; } const contractAddress = query.split('/')[1]; navigateToMintCollection(contractAddress, network); From 5b55e974eea9d1853050b2692f7c5298723a3561 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 10:44:22 -0400 Subject: [PATCH 36/44] i18n + analytics clean up --- src/analytics/event.ts | 26 ++++++++++++++++---------- src/languages/en_US.json | 5 ++++- src/screens/mints/MintSheet.tsx | 29 ++++++++++++++++------------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/analytics/event.ts b/src/analytics/event.ts index 3e457e010b1..d80447c2eb6 100644 --- a/src/analytics/event.ts +++ b/src/analytics/event.ts @@ -79,10 +79,6 @@ export const event = { nftOffersSelectedSortCriterion: 'Selected NFT Offers Sort Criterion', nftOffersAcceptedOffer: 'Accepted NFT Offer', - nftMintsOpenedSheet: 'Opened NFT Mint Sheet', - nftMintsMintingNFT: 'Minting NFT', - nftMintsMintedNFT: 'Minted NFT', - poapsOpenedMintSheet: 'Opened POAP mint sheet', poapsMintedPoap: 'Minted POAP', poapsViewedOnPoap: 'Viewed POAP on poap.gallery', @@ -95,6 +91,11 @@ export const event = { mintsPressedMintButton: 'Pressed mint button in mints sheet', mintsPressedViewAllMintsButton: 'Pressed view all mints button in mints card', mintsChangedFilter: 'Changed mints filter', + + mintsOpenedSheet: 'Opened NFT Mint Sheet', + mintsOpeningMintDotFun: 'Opening Mintdotfun', + mintsMintingNFT: 'Minting NFT', + mintsMintedNFT: 'Minted NFT', } as const; /** @@ -298,21 +299,26 @@ export type EventProperties = { rainbowFee: number; offerCurrency: { symbol: string; contractAddress: string }; }; - [event.nftMintsMintingNFT]: { + [event.mintsMintingNFT]: { contract: string; - network: Network; + chainId: number; quantity: number; collectionName: string; }; - [event.nftMintsMintedNFT]: { + [event.mintsMintedNFT]: { contract: string; - network: Network; + chainId: number; quantity: number; collectionName: string; }; - [event.nftMintsOpenedSheet]: { + [event.mintsOpenedSheet]: { contract: string; - network: Network; + chainId: number; + collectionName: string; + }; + [event.mintsOpeningMintDotFun]: { + contract: string; + chainId: number; collectionName: string; }; [event.poapsMintedPoap]: { diff --git a/src/languages/en_US.json b/src/languages/en_US.json index 49e465c3b9e..f3eda6350d8 100644 --- a/src/languages/en_US.json +++ b/src/languages/en_US.json @@ -1163,6 +1163,8 @@ "free": "Free", "by": "By", "unknown": "unknown", + "max" : "Max", + "nft_count": "%{number} NFTs", "description": "Description", "total_minted": "Total Minted", "first_event": "First Event", @@ -1171,7 +1173,8 @@ "contract": "Contract", "network": "Network", "could_not_find_collection": "Could not find collection", - "unable_to_find_check_again": "We are unable to find this collection, double check the address and network or try again later" + "unable_to_find_check_again": "We are unable to find this collection, double check the address and network or try again later", + "mintdotfun_unsupported_network": "Mint.fun does not support this network" }, "nfts": { "selling": "Selling" diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 3a7433f392e..a245280c389 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -165,8 +165,7 @@ const MintSheet = () => { const [showNativePrice, setShowNativePrice] = useState(false); const [gasError, setGasError] = useState(false); const currentNetwork = - RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || - Network.mainnet; + RainbowNetworks.find(({ id }) => id === vvv)?.value || Network.mainnet; const [ensName, setENSName] = useState(''); const [mintStatus, setMintStatus] = useState< 'none' | 'minting' | 'minted' | 'error' @@ -253,7 +252,7 @@ const MintSheet = () => { analyticsV2.track(event.nftMintsOpenedSheet, { collectionName: mintCollection?.name, contract: mintCollection.id, - network: currentNetwork, + chainId: mintCollection.chainId, }); } }); @@ -271,7 +270,6 @@ const MintSheet = () => { const txFee = getTotalGasPrice(); const totalMintPrice = multiply(price.amount, quantity); // gas price + mint price - // TODO: need to double check this when there are paid mints available setInsufficientEth( greaterThanOrEqualTo(add(txFee, totalMintPrice), nativeBalance) ); @@ -389,10 +387,7 @@ const MintSheet = () => { Network.zora, ]; if (!MintDotFunNetworks.includes(network)) { - // show alert mint.fun does not support - // i18n - // this isnt possible with our current entry points - Alert.alert('Mint.fun does not support this network'); + Alert.alert(i18n.t(i18n.l.minting.mintdotfun_unsupported_network)); } let chainSlug = 'ethereum'; @@ -418,15 +413,20 @@ const MintSheet = () => { // link to mint.fun if reservoir not supporting if (!isMintingAvailable) { + analyticsV2.track(event.mintsOpeningMintDotFun, { + collectionName: mintCollection.name || '', + contract: mintCollection.id || '', + chainId: mintCollection.chainId, + }); Linking.openURL(buildMintDotFunUrl(mintCollection.id!, currentNetwork)); return; } logger.info('Minting NFT', { name: mintCollection.name }); - analyticsV2.track(event.nftMintsMintingNFT, { + analyticsV2.track(event.mintsMintingNFT, { collectionName: mintCollection.name || '', contract: mintCollection.id || '', - network: currentNetwork, + chainId: mintCollection.chainId, quantity, }); setMintStatus('minting'); @@ -497,7 +497,7 @@ const MintSheet = () => { analyticsV2.track(event.nftMintsMintedNFT, { collectionName: mintCollection.name || '', contract: mintCollection.id || '', - network: currentNetwork, + chainId: mintCollection.chainId, quantity, }); navigate(Routes.PROFILE_SCREEN); @@ -688,9 +688,10 @@ const MintSheet = () => { align="center" size="13pt" weight="semibold" + numberOfLines={1} > {quantity === Number(maxMintsPerWallet) - ? 'Max' + ? i18n.t(i18n.l.minting.max) : ''} } @@ -761,7 +762,9 @@ const MintSheet = () => { size="17pt" weight="medium" > - {`${mintCollection.tokenCount} NFTs`} + {i18n.t(i18n.l.minting.nft_count, { + number: mintCollection.tokenCount, + })} } /> From 35e13203b7be969f563a394918c100b31daa3a62 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 10:44:39 -0400 Subject: [PATCH 37/44] debug logging --- src/resources/reservoir/mints.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/resources/reservoir/mints.ts b/src/resources/reservoir/mints.ts index 3d168f92819..5d8b1d1af40 100644 --- a/src/resources/reservoir/mints.ts +++ b/src/resources/reservoir/mints.ts @@ -22,6 +22,10 @@ export const navigateToMintCollection = async ( contractAddress: EthereumAddress, network: Network ) => { + logger.debug('Mints: Navigating to Mint Collection', { + contractAddress, + network, + }); try { const chainId = getNetworkObj(network).id; const res = await client.getReservoirCollection({ @@ -33,10 +37,15 @@ export const navigateToMintCollection = async ( collection: res.getReservoirCollection?.collection, }); } else { + logger.warn('Mints: No collection found', { contractAddress, network }); showAlert(); } } catch (e) { - console.log(e); + logger.warn('Mints: navigateToMintCollection error', { + contractAddress, + network, + error: e, + }); showAlert(); } }; From 21e4798b81524ba4131a35e580fdda8edd98a541 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 10:46:53 -0400 Subject: [PATCH 38/44] format + lint --- src/languages/en_US.json | 2 +- src/screens/mints/MintSheet.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/languages/en_US.json b/src/languages/en_US.json index f3eda6350d8..3e7b21e644a 100644 --- a/src/languages/en_US.json +++ b/src/languages/en_US.json @@ -1163,7 +1163,7 @@ "free": "Free", "by": "By", "unknown": "unknown", - "max" : "Max", + "max": "Max", "nft_count": "%{number} NFTs", "description": "Description", "total_minted": "Total Minted", diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index a245280c389..e52d4706f35 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -165,7 +165,8 @@ const MintSheet = () => { const [showNativePrice, setShowNativePrice] = useState(false); const [gasError, setGasError] = useState(false); const currentNetwork = - RainbowNetworks.find(({ id }) => id === vvv)?.value || Network.mainnet; + RainbowNetworks.find(({ id }) => id === mintCollection.chainId)?.value || + Network.mainnet; const [ensName, setENSName] = useState(''); const [mintStatus, setMintStatus] = useState< 'none' | 'minting' | 'minted' | 'error' @@ -249,7 +250,7 @@ const MintSheet = () => { useFocusEffect(() => { if (mintCollection.name && mintCollection.id) { - analyticsV2.track(event.nftMintsOpenedSheet, { + analyticsV2.track(event.mintsOpenedSheet, { collectionName: mintCollection?.name, contract: mintCollection.id, chainId: mintCollection.chainId, @@ -494,7 +495,7 @@ const MintSheet = () => { txRef.current = tx.hash; // @ts-expect-error TODO: fix when we overhaul tx list, types are not good dispatch(dataAddNewTransaction(tx)); - analyticsV2.track(event.nftMintsMintedNFT, { + analyticsV2.track(event.mintsMintedNFT, { collectionName: mintCollection.name || '', contract: mintCollection.id || '', chainId: mintCollection.chainId, From 60bd8b3d8e4d61e30d5ecdbacde47399bf9d3a6c Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 10:49:32 -0400 Subject: [PATCH 39/44] priceInEth --- src/analytics/event.ts | 2 ++ src/screens/mints/MintSheet.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/analytics/event.ts b/src/analytics/event.ts index d80447c2eb6..b6db281fcf0 100644 --- a/src/analytics/event.ts +++ b/src/analytics/event.ts @@ -304,12 +304,14 @@ export type EventProperties = { chainId: number; quantity: number; collectionName: string; + priceInEth: string; }; [event.mintsMintedNFT]: { contract: string; chainId: number; quantity: number; collectionName: string; + priceInEth: string; }; [event.mintsOpenedSheet]: { contract: string; diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index e52d4706f35..5d04153cb2e 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -429,6 +429,7 @@ const MintSheet = () => { contract: mintCollection.id || '', chainId: mintCollection.chainId, quantity, + priceInEth: mintPriceAmount, }); setMintStatus('minting'); @@ -500,6 +501,7 @@ const MintSheet = () => { contract: mintCollection.id || '', chainId: mintCollection.chainId, quantity, + priceInEth: mintPriceAmount, }); navigate(Routes.PROFILE_SCREEN); setMintStatus('minted'); From b15a9552d48ac51297c6754198f8d4ec200ef36f Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 11:59:09 -0400 Subject: [PATCH 40/44] fix disabled state --- src/screens/mints/MintSheet.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 5d04153cb2e..86d4580fb8f 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -716,7 +716,6 @@ const MintSheet = () => { backgroundColor={ mintStatus === 'error' ? colors.red : imageColor } - disabled={!isSufficientGas || !isValidGas} hideInnerBorder label={buttonLabel} onLongPress={actionOnPress} From b886fa125e98d1b9833089eb7cd188dbb8c30431 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 12:26:04 -0400 Subject: [PATCH 41/44] add error event --- src/analytics/event.ts | 8 ++++++++ src/screens/mints/MintSheet.tsx | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/analytics/event.ts b/src/analytics/event.ts index b6db281fcf0..5af645fe65a 100644 --- a/src/analytics/event.ts +++ b/src/analytics/event.ts @@ -96,6 +96,7 @@ export const event = { mintsOpeningMintDotFun: 'Opening Mintdotfun', mintsMintingNFT: 'Minting NFT', mintsMintedNFT: 'Minted NFT', + mintsErrorMintingNFT: 'Error Minting NFT', } as const; /** @@ -313,6 +314,13 @@ export type EventProperties = { collectionName: string; priceInEth: string; }; + [event.mintsErrorMintingNFT]: { + contract: string; + chainId: number; + quantity: number; + collectionName: string; + priceInEth: string; + }; [event.mintsOpenedSheet]: { contract: string; chainId: number; diff --git a/src/screens/mints/MintSheet.tsx b/src/screens/mints/MintSheet.tsx index 86d4580fb8f..1682a368b15 100644 --- a/src/screens/mints/MintSheet.tsx +++ b/src/screens/mints/MintSheet.tsx @@ -512,6 +512,13 @@ const MintSheet = () => { }); } catch (e) { setMintStatus('error'); + analyticsV2.track(event.mintsErrorMintingNFT, { + collectionName: mintCollection.name || '', + contract: mintCollection.id || '', + chainId: mintCollection.chainId, + quantity, + priceInEth: mintPriceAmount, + }); logger.error( new RainbowError(`Error minting NFT: ${(e as Error).message}`) ); @@ -524,6 +531,7 @@ const MintSheet = () => { imageUrl, isMintingAvailable, isReadOnlyWallet, + mintCollection.chainId, mintCollection.id, mintCollection.name, mintPriceAmount, From bdcf4c692ccf407eea50dd1064ab684cea13eaa4 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 17:07:43 -0400 Subject: [PATCH 42/44] revert to prod --- src/graphql/config.js | 2 +- src/graphql/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graphql/config.js b/src/graphql/config.js index 30f220bef11..69bbea91490 100644 --- a/src/graphql/config.js +++ b/src/graphql/config.js @@ -17,7 +17,7 @@ exports.config = { document: './queries/arc.graphql', schema: { method: 'GET', - url: 'https://arc-graphql.rainbowdotme.workers.dev/graphql', + url: 'https://arc-graphql.rainbow.me/graphql', headers: { 'x-api-key': 'ARC_GRAPHQL_API_KEY', }, diff --git a/src/graphql/index.ts b/src/graphql/index.ts index 8ea3ac47ffb..16f276838c0 100644 --- a/src/graphql/index.ts +++ b/src/graphql/index.ts @@ -9,5 +9,5 @@ export const ensClient = getEnsSdk(getFetchRequester(config.ens)); export const metadataClient = getMetadataSdk( getFetchRequester(config.metadata) ); -export const arcClient = getArcSdk(getFetchRequester(config.arcDev)); +export const arcClient = getArcSdk(getFetchRequester(config.arc)); export const arcDevClient = getArcDevSdk(getFetchRequester(config.arcDev)); From ed6bd58a1c216f3ccc1fb44773f52bf7a5b97a02 Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 17:08:03 -0400 Subject: [PATCH 43/44] callback --- src/hooks/useGas.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useGas.ts b/src/hooks/useGas.ts index 7d1477a3f6e..4e38fe7ffa0 100644 --- a/src/hooks/useGas.ts +++ b/src/hooks/useGas.ts @@ -186,7 +186,7 @@ export default function useGas({ [dispatch] ); - const getTotalGasPrice = () => { + const getTotalGasPrice = useCallback(() => { const txFee = gasData?.selectedGasFee?.gasFee; const isLegacyGasNetwork = getNetworkObj(gasData?.txNetwork).gas.gasType === 'legacy'; @@ -196,7 +196,7 @@ export default function useGas({ const txFeeAmount = fromWei(txFeeValue?.value?.amount); return txFeeAmount; - }; + }, [gasData?.selectedGasFee?.gasFee, gasData?.txNetwork]); return { isGasReady, From 4241c16ab60896bba839bdca8af47ad29bde856b Mon Sep 17 00:00:00 2001 From: skylarbarrera Date: Thu, 12 Oct 2023 17:08:15 -0400 Subject: [PATCH 44/44] oops --- src/resources/nfts/simplehash/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/nfts/simplehash/utils.ts b/src/resources/nfts/simplehash/utils.ts index 7e577893710..4f1070cfa26 100644 --- a/src/resources/nfts/simplehash/utils.ts +++ b/src/resources/nfts/simplehash/utils.ts @@ -435,7 +435,7 @@ export function simpleHashNFTToInternalNFT(nft: ValidatedSimpleHashNFT): NFT { externalUrl: collection.external_url ?? undefined, floorPrices, imageUrl: collection.image_url - ? maybeSignUri(collection.image_url, { w: 100 }) + ? maybeSignUri(collection.image_url) : undefined, name: uniqueTokenType === uniqueTokenTypes.ENS