From ddf39fe95cb3a58cf3ea6694a7ca64e7ab39d305 Mon Sep 17 00:00:00 2001 From: Fricoben Date: Thu, 12 Sep 2024 13:44:34 -0300 Subject: [PATCH] ref: free renewal adaptation --- components/discount/freeRenewalCheckout.tsx | 160 +++++--------------- hooks/checkout/useFreeRenewalTxPrep.tsx | 123 +++++++++++++++ utils/apiWrappers/identity.ts | 5 +- utils/discounts/freeRenewal.ts | 2 +- 4 files changed, 167 insertions(+), 123 deletions(-) create mode 100644 hooks/checkout/useFreeRenewalTxPrep.tsx diff --git a/components/discount/freeRenewalCheckout.tsx b/components/discount/freeRenewalCheckout.tsx index 966ccfa3..8cdfdfa4 100644 --- a/components/discount/freeRenewalCheckout.tsx +++ b/components/discount/freeRenewalCheckout.tsx @@ -2,14 +2,8 @@ import React from "react"; import { FunctionComponent, useEffect, useState } from "react"; import Button from "../UI/button"; import { useAccount, useContractWrite } from "@starknet-react/core"; -import { - formatHexString, - isValidEmail, - selectedDomainsToArray, - selectedDomainsToEncodedArray, -} from "../../utils/stringService"; +import { formatHexString, isValidEmail } from "../../utils/stringService"; import { applyRateToBigInt } from "../../utils/feltService"; -import { Call } from "starknet"; import styles from "../../styles/components/registerV2.module.css"; import TextField from "../UI/textField"; import SwissForm from "../domains/swissForm"; @@ -18,17 +12,13 @@ import RegisterSummary from "../domains/registerSummary"; import { computeMetadataHash, generateSalt } from "../../utils/userDataService"; import { areDomainSelected, - getApprovalAmount, getDisplayablePrice, } from "../../utils/priceService"; import RenewalDomainsBox from "../domains/renewalDomainsBox"; -import registrationCalls from "../../utils/callData/registrationCalls"; -import autoRenewalCalls from "../../utils/callData/autoRenewalCalls"; import BackButton from "../UI/backButton"; import { useRouter } from "next/router"; import { useNotificationManager } from "../../hooks/useNotificationManager"; import { - AutoRenewalContracts, CurrencyType, ERC20Contract, NotificationType, @@ -36,18 +26,11 @@ import { swissVatRate, } from "../../utils/constants"; import RegisterCheckboxes from "../domains/registerCheckboxes"; -import { utils } from "starknetid.js"; import RegisterConfirmationModal from "../UI/registerConfirmationModal"; -import useNeedsAllowances from "../../hooks/useNeedAllowances"; import ConnectButton from "../UI/connectButton"; -import { - getAutoRenewAllowance, - getDomainPrice, - getTokenQuote, - getTotalYearlyPrice, -} from "../../utils/altcoinService"; +import { getTokenQuote, getTotalYearlyPrice } from "../../utils/altcoinService"; import { areArraysEqual } from "@/utils/arrayService"; -import useNeedSubscription from "@/hooks/useNeedSubscription"; +import { useFreeRenewalTxPrep } from "@/hooks/checkout/useFreeRenewalTxPrep"; type FreeRenewalCheckoutProps = { groups: string[]; @@ -65,7 +48,6 @@ const FreeRenewalCheckout: FunctionComponent = ({ const [isSwissResident, setIsSwissResident] = useState(false); const [salesTaxRate, setSalesTaxRate] = useState(0); const [salesTaxAmount, setSalesTaxAmount] = useState(BigInt(0)); - const [callData, setCallData] = useState([]); // price paid by the user including discount const [quoteData, setQuoteData] = useState(null); // null if in ETH const [displayedCurrencies, setDisplayedCurrencies] = useState< @@ -76,13 +58,28 @@ const FreeRenewalCheckout: FunctionComponent = ({ const [salt, setSalt] = useState(); const [metadataHash, setMetadataHash] = useState(); const [needMetadata, setNeedMetadata] = useState(false); - const [potentialPrice, setPotentialPrice] = useState(BigInt(0)); + const [potentialPrices, setPotentialPrices] = useState< + Record + >({ + [CurrencyType.ETH]: BigInt(0), + [CurrencyType.STRK]: BigInt(0), + }); const [customCheckoutMessage, setCustomCheckoutMessage] = useState(""); const [selectedDomains, setSelectedDomains] = useState>(); const { address } = useAccount(); + const { callData } = useFreeRenewalTxPrep( + quoteData, + salesTaxAmount, + displayedCurrencies, + salesTaxRate, + potentialPrices, + selectedDomains, + address, + metadataHash + ); const { writeAsync: execute, data: renewData } = useContractWrite({ calls: callData, }); @@ -91,8 +88,6 @@ const FreeRenewalCheckout: FunctionComponent = ({ const { addTransaction } = useNotificationManager(); const router = useRouter(); const [loadingPrice, setLoadingPrice] = useState(false); - const allowanceStatus = useNeedsAllowances(address); - const needSubscription = useNeedSubscription(address); useEffect(() => { if (!renewData?.transaction_hash || !salt || !metadataHash) return; @@ -234,95 +229,14 @@ const FreeRenewalCheckout: FunctionComponent = ({ useEffect(() => { if (isSwissResident) { setSalesTaxRate(swissVatRate); - setSalesTaxAmount(applyRateToBigInt(potentialPrice, swissVatRate)); + setSalesTaxAmount( + applyRateToBigInt(potentialPrices[CurrencyType.ETH], swissVatRate) + ); } else { setSalesTaxRate(0); setSalesTaxAmount(BigInt(0)); } - }, [isSwissResident, potentialPrice]); - - // build free renewal call - useEffect(() => { - const isCurrencyETH = areArraysEqual(displayedCurrencies, [ - CurrencyType.ETH, - ]); - - if (!isCurrencyETH && !quoteData) return; - const txMetadataHash = `0x${metadataHash}` as HexString; - if (selectedDomains) { - // Free renewal - const calls = [ - ...registrationCalls.multiCallFreeRenewals( - selectedDomainsToEncodedArray(selectedDomains), - AutoRenewalContracts[displayedCurrencies[0]] // If we have multiple currencies, we use the first one (which is ETH) - ), - ]; - - displayedCurrencies.map((currency) => { - // Add ERC20 allowance for all currencies if needed - if (allowanceStatus[currency].needsAllowance) { - const amountToApprove = getApprovalAmount( - potentialPrice, - salesTaxAmount, - 1, - allowanceStatus[currency].currentAllowance - ); - - calls.unshift( - autoRenewalCalls.approve( - ERC20Contract[currency], - AutoRenewalContracts[currency], - amountToApprove - ) - ); - } - // Add AutoRenewal calls for all currencies - selectedDomainsToArray(selectedDomains).map((domain) => { - if ( - needSubscription && - needSubscription.needSubscription[domain]?.[currency] - ) { - const encodedDomain = utils - .encodeDomain(domain) - .map((element) => element.toString())[0]; - - const domainPrice = getDomainPrice( - domain, - currency, - 365, - quoteData?.quote - ); - const allowance = getAutoRenewAllowance( - currency, - salesTaxRate, - domainPrice - ); - - calls.unshift( - autoRenewalCalls.enableRenewal( - AutoRenewalContracts[currency], - encodedDomain, - allowance, // Warning review: Do we need to multiply by 10 here to have 10 years ? - txMetadataHash - ) - ); - } - }); - }); - setCallData(calls); - } - }, [ - selectedDomains, - salesTaxAmount, - allowanceStatus, - metadataHash, - salesTaxRate, - needMetadata, - quoteData, - displayedCurrencies, - needSubscription, - potentialPrice, - ]); + }, [isSwissResident, potentialPrices]); useEffect(() => { const isCurrencySTRK = areArraysEqual(displayedCurrencies, [ @@ -331,20 +245,28 @@ const FreeRenewalCheckout: FunctionComponent = ({ const currencyDisplayed = isCurrencySTRK ? CurrencyType.STRK : CurrencyType.ETH; - const totalYearlyPrice = getTotalYearlyPrice( - selectedDomains, - currencyDisplayed, - quoteData?.quote - ); + + const newPotentialPrices = displayedCurrencies.reduce((acc, currency) => { + const totalYearlyPrice = getTotalYearlyPrice( + selectedDomains, + currency, + quoteData?.quote + ); + acc[currency] = totalYearlyPrice; + return acc; + }, {} as Record); + + setPotentialPrices((prevPrices) => ({ + ...prevPrices, + ...newPotentialPrices, + })); + const potentialCurrency = quoteData?.quote ? currencyDisplayed : CurrencyType.ETH; - - // Setting the potential price in the displayed currency - setPotentialPrice(totalYearlyPrice); setCustomCheckoutMessage( `${offer.customMessage} and then ${getDisplayablePrice( - totalYearlyPrice + newPotentialPrices[potentialCurrency] ?? BigInt(0) )} ${potentialCurrency} per year` ); @@ -395,7 +317,7 @@ const FreeRenewalCheckout: FunctionComponent = ({
, + selectedDomains?: Record, + address?: string, + metadataHash?: string +) => { + const [callData, setCallData] = useState([]); + const allowanceStatus = useNeedsAllowances(address); + const needSubscription = useNeedSubscription(address); + + const isCurrencyETH = useMemo( + () => areArraysEqual(displayedCurrencies, [CurrencyType.ETH]), + [displayedCurrencies] + ); + + const buildCalls = useCallback(() => { + if (!isCurrencyETH && !quoteData) return []; + const txMetadataHash = `0x${metadataHash}` as HexString; + if (!selectedDomains) return []; + + const calls: Call[] = [ + ...registrationCalls.multiCallFreeRenewals( + selectedDomainsToEncodedArray(selectedDomains), + AutoRenewalContracts[displayedCurrencies[0]] + ), + ]; + + displayedCurrencies.forEach((currency) => { + if (allowanceStatus[currency].needsAllowance) { + const amountToApprove = getApprovalAmount( + potentialPrices[currency], + salesTaxAmount, + 1, + allowanceStatus[currency].currentAllowance + ); + + calls.unshift( + autoRenewalCalls.approve( + ERC20Contract[currency], + AutoRenewalContracts[currency], + amountToApprove + ) + ); + } + + selectedDomainsToArray(selectedDomains).forEach((domain) => { + if (needSubscription.needSubscription[domain]?.[currency]) { + const encodedDomain = utils + .encodeDomain(domain) + .map((element) => element.toString())[0]; + + const domainPrice = getDomainPrice( + domain, + currency, + 365, + quoteData?.quote + ); + const allowance = getAutoRenewAllowance( + currency, + salesTaxRate, + domainPrice + ); + + calls.unshift( + autoRenewalCalls.enableRenewal( + AutoRenewalContracts[currency], + encodedDomain, + allowance, + txMetadataHash + ) + ); + } + }); + }); + + return calls; + }, [ + isCurrencyETH, + quoteData, + selectedDomains, + displayedCurrencies, + allowanceStatus, + potentialPrices, + salesTaxAmount, + needSubscription, + salesTaxRate, + metadataHash, + ]); + + useEffect(() => { + const newCallData = buildCalls(); + if (!isEqual(newCallData, callData)) { + setCallData(newCallData); + } + }, [buildCalls, callData]); + + return { callData }; +}; diff --git a/utils/apiWrappers/identity.ts b/utils/apiWrappers/identity.ts index 292b5fe0..06fa7bbe 100644 --- a/utils/apiWrappers/identity.ts +++ b/utils/apiWrappers/identity.ts @@ -1,4 +1,3 @@ -import { shortString } from "starknet"; import { fromUint256, hexToDecimal } from "../feltService"; import { formatHexString, getImgUrl } from "../stringService"; import { @@ -84,7 +83,7 @@ export class Identity { } get targetAddress(): string { - let legacyAddress = this._data.domain?.legacy_address; + const legacyAddress = this._data.domain?.legacy_address; if ( legacyAddress && legacyAddress !== @@ -92,7 +91,7 @@ export class Identity { ) { return legacyAddress; } - let starknetFromId = this.getUserData(STARKNET); + const starknetFromId = this.getUserData(STARKNET); if ( starknetFromId && starknetFromId !== diff --git a/utils/discounts/freeRenewal.ts b/utils/discounts/freeRenewal.ts index 058f0f7a..a2330d2b 100644 --- a/utils/discounts/freeRenewal.ts +++ b/utils/discounts/freeRenewal.ts @@ -10,7 +10,7 @@ type FreeRenewalDiscount = { export const freeRenewalDiscount: FreeRenewalDiscount = { name: "The Free Renewal", image: "/freeRenewal/freeRenewal.webp", - expiry: 1816767999000, // timestamp in ms + expiry: 1727308799000, // timestamp in ms 25/09/2024 23:59:59 discountMailGroupId: "106085143136961963", offer: { durationInDays: 90, // in days