Skip to content

Commit

Permalink
ref: free renewal adaptation
Browse files Browse the repository at this point in the history
  • Loading branch information
fricoben committed Sep 12, 2024
1 parent 5eb1ee2 commit ddf39fe
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 123 deletions.
160 changes: 41 additions & 119 deletions components/discount/freeRenewalCheckout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -18,36 +12,25 @@ 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,
TransactionType,
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[];
Expand All @@ -65,7 +48,6 @@ const FreeRenewalCheckout: FunctionComponent<FreeRenewalCheckoutProps> = ({
const [isSwissResident, setIsSwissResident] = useState<boolean>(false);
const [salesTaxRate, setSalesTaxRate] = useState<number>(0);
const [salesTaxAmount, setSalesTaxAmount] = useState<bigint>(BigInt(0));
const [callData, setCallData] = useState<Call[]>([]);
// price paid by the user including discount
const [quoteData, setQuoteData] = useState<QuoteQueryData | null>(null); // null if in ETH
const [displayedCurrencies, setDisplayedCurrencies] = useState<
Expand All @@ -76,13 +58,28 @@ const FreeRenewalCheckout: FunctionComponent<FreeRenewalCheckoutProps> = ({
const [salt, setSalt] = useState<string | undefined>();
const [metadataHash, setMetadataHash] = useState<string | undefined>();
const [needMetadata, setNeedMetadata] = useState<boolean>(false);
const [potentialPrice, setPotentialPrice] = useState<bigint>(BigInt(0));
const [potentialPrices, setPotentialPrices] = useState<
Record<CurrencyType, bigint>
>({
[CurrencyType.ETH]: BigInt(0),
[CurrencyType.STRK]: BigInt(0),
});
const [customCheckoutMessage, setCustomCheckoutMessage] =
useState<string>("");

const [selectedDomains, setSelectedDomains] =
useState<Record<string, boolean>>();
const { address } = useAccount();
const { callData } = useFreeRenewalTxPrep(
quoteData,
salesTaxAmount,
displayedCurrencies,
salesTaxRate,
potentialPrices,
selectedDomains,
address,
metadataHash
);
const { writeAsync: execute, data: renewData } = useContractWrite({
calls: callData,
});
Expand All @@ -91,8 +88,6 @@ const FreeRenewalCheckout: FunctionComponent<FreeRenewalCheckoutProps> = ({
const { addTransaction } = useNotificationManager();
const router = useRouter();
const [loadingPrice, setLoadingPrice] = useState<boolean>(false);
const allowanceStatus = useNeedsAllowances(address);
const needSubscription = useNeedSubscription(address);

useEffect(() => {
if (!renewData?.transaction_hash || !salt || !metadataHash) return;
Expand Down Expand Up @@ -234,95 +229,14 @@ const FreeRenewalCheckout: FunctionComponent<FreeRenewalCheckoutProps> = ({
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, [
Expand All @@ -331,20 +245,28 @@ const FreeRenewalCheckout: FunctionComponent<FreeRenewalCheckoutProps> = ({
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<CurrencyType, bigint>);

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`
);

Expand Down Expand Up @@ -395,7 +317,7 @@ const FreeRenewalCheckout: FunctionComponent<FreeRenewalCheckoutProps> = ({
</div>
<div className={styles.summary}>
<RegisterSummary
priceInEth={potentialPrice}
priceInEth={potentialPrices[CurrencyType.ETH]}
price={offer.price}
durationInYears={offer.durationInDays / 365}
salesTaxRate={salesTaxRate}
Expand Down
123 changes: 123 additions & 0 deletions hooks/checkout/useFreeRenewalTxPrep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useState, useEffect, useMemo, useCallback } from "react";
import {
AutoRenewalContracts,
CurrencyType,
ERC20Contract,
} from "@/utils/constants";
import { Call } from "starknet";
import registrationCalls from "@/utils/callData/registrationCalls";
import { utils } from "starknetid.js";
import { getAutoRenewAllowance, getDomainPrice } from "@/utils/altcoinService";
import { getApprovalAmount } from "@/utils/priceService";
import autoRenewalCalls from "@/utils/callData/autoRenewalCalls";
import {
selectedDomainsToArray,
selectedDomainsToEncodedArray,
} from "@/utils/stringService";
import useNeedsAllowances from "../useNeedAllowances";
import useNeedSubscription from "../useNeedSubscription";
import { areArraysEqual } from "@/utils/arrayService";
import { isEqual } from "lodash";

export const useFreeRenewalTxPrep = (
quoteData: QuoteQueryData | null,
salesTaxAmount: bigint,
displayedCurrencies: CurrencyType[],
salesTaxRate: number,
potentialPrices: Record<CurrencyType, bigint>,
selectedDomains?: Record<string, boolean>,
address?: string,
metadataHash?: string
) => {
const [callData, setCallData] = useState<Call[]>([]);
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 };
};
5 changes: 2 additions & 3 deletions utils/apiWrappers/identity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { shortString } from "starknet";
import { fromUint256, hexToDecimal } from "../feltService";
import { formatHexString, getImgUrl } from "../stringService";
import {
Expand Down Expand Up @@ -84,15 +83,15 @@ export class Identity {
}

get targetAddress(): string {
let legacyAddress = this._data.domain?.legacy_address;
const legacyAddress = this._data.domain?.legacy_address;
if (
legacyAddress &&
legacyAddress !==
"0x0000000000000000000000000000000000000000000000000000000000000000"
) {
return legacyAddress;
}
let starknetFromId = this.getUserData(STARKNET);
const starknetFromId = this.getUserData(STARKNET);
if (
starknetFromId &&
starknetFromId !==
Expand Down
Loading

0 comments on commit ddf39fe

Please sign in to comment.