Skip to content

Commit

Permalink
feat: free registration starknetCC problems (#854)
Browse files Browse the repository at this point in the history
* feat: free registration starknetCC problems

* ready for whitelist

* improving connect menu

* moving to navbar

* working for braavos + fix non explicit "loading gas" message

* showing errors in console

* disable button if no coupon

* more detailed error message

* refreshing when switching account

* improving tx validity detection

* making code more resilient

* Update hooks/isDeployed.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* improving deployment data gathering

* improving support for argent mobile

* improving support for argent mobile

* disabling paymaster for argent mobile

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
Marchand-Nicolas and coderabbitai[bot] authored Jul 22, 2024
1 parent 88da332 commit 8f3e3d2
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 243 deletions.
2 changes: 1 addition & 1 deletion components/UI/connectButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const ConnectButton: FunctionComponent = () => {
className={styles.btnIcon}
/>
) : null}
<p>Connect wallet</p>
<p className="mx-auto">Connect wallet</p>
{lastConnector ? (
<div
className={styles.arrowDown}
Expand Down
8 changes: 8 additions & 0 deletions components/UI/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import WalletConnect from "./walletConnect";
import ArrowDownIcon from "./iconsComponents/icons/arrowDownIcon";
import errorLottie from "../../public/visuals/errorLottie.json";
import { bigintToStringHex } from "@/utils/stringService";
import { useRouter } from "next/router";

const Navbar: FunctionComponent = () => {
const theme = useTheme();
Expand All @@ -53,6 +54,13 @@ const Navbar: FunctionComponent = () => {
const { starknetIdNavigator } = useContext(StarknetIdJsContext);
const [showWalletConnectModal, setShowWalletConnectModal] =
useState<boolean>(false);
const router = useRouter();

useEffect(() => {
const pageName = router.pathname.split("/")[1];
if (pageName !== "gift" && pageName !== "register") return;
if (isMobile) setShowWalletConnectModal(true);
}, [isMobile, router.pathname]);

const [lastConnector, setLastConnector] = useState<Connector | null>(null);
// could be replaced by a useProfileData from starknet-react when updated
Expand Down
69 changes: 30 additions & 39 deletions components/discount/freeRegisterCheckout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,21 @@ const FreeRegisterCheckout: FunctionComponent<FreeRegisterCheckoutProps> = ({
handleRegister,
data: registerData,
paymasterRewards,
gasTokenPrices,
gasTokenPrice,
loadingGas,
gasMethod,
setGasMethod,
gaslessCompatibility,
setGasTokenPrice,
sponsoredDeploymentAvailable,
maxGasTokenAmount,
loadingDeploymentData,
} = usePaymaster(callData, async (transactionHash) => {
setDomainsMinting((prev) =>
new Map(prev).set(encodedDomain.toString(), true)
);
console.log(transactionHash);
if (transactionHash) setTransactionHash(transactionHash);
});
refreshRewards,
invalidTx,
loadingTypedData,
} = usePaymaster(
callData,
async (transactionHash) => {
setDomainsMinting((prev) =>
new Map(prev).set(encodedDomain.toString(), true)
);
if (transactionHash) setTransactionHash(transactionHash);
},
!coupon
);

useEffect(() => {
if (!registerData?.transaction_hash) return;
Expand All @@ -95,6 +93,11 @@ const FreeRegisterCheckout: FunctionComponent<FreeRegisterCheckoutProps> = ({
if (address) setTargetAddress(address);
}, [address]);

useEffect(() => {
if (loadingCoupon || !coupon) return;
refreshRewards();
}, [loadingCoupon, coupon, refreshRewards, address]);

useEffect(() => {
// salt must not be empty to preserve privacy
if (!salt) return;
Expand All @@ -104,11 +107,11 @@ const FreeRegisterCheckout: FunctionComponent<FreeRegisterCheckoutProps> = ({
}, [salt]);

useEffect(() => {
if (signature[0] === null) return;
// Variables
const newTokenId: number = Math.floor(Math.random() * 1000000000000);
setTokenId(newTokenId);
const txMetadataHash = `0x${metadataHash}` as HexString;

const freeRegisterCalls = registrationCalls.getFreeRegistrationCalls(
newTokenId,
encodedDomain,
Expand Down Expand Up @@ -142,11 +145,7 @@ const FreeRegisterCheckout: FunctionComponent<FreeRegisterCheckoutProps> = ({
}, [transactionHash, tokenId]);

useEffect(() => {
if (!coupon) {
setCouponError("Please enter a coupon code");
setLoadingCoupon(false);
return;
}
if (!coupon) return setLoadingCoupon(false);
const lastSuccessCoupon = localStorage.getItem("lastSuccessCoupon");
if (coupon === lastSuccessCoupon) {
setCouponError("");
Expand Down Expand Up @@ -199,21 +198,7 @@ const FreeRegisterCheckout: FunctionComponent<FreeRegisterCheckoutProps> = ({
</div>
</div>
<div className={styles.summary}>
<FreeRegisterSummary
duration={duration}
domain={domain}
hasPaymasterRewards={paymasterRewards.length > 0}
gasTokenPrices={gasTokenPrices}
gasTokenPrice={gasTokenPrice}
setGasTokenPrice={setGasTokenPrice}
gasMethod={gasMethod}
setGasMethod={setGasMethod}
paymasterAvailable={
gaslessCompatibility?.isCompatible || sponsoredDeploymentAvailable
}
maxGasTokenAmount={maxGasTokenAmount}
deployed={gaslessCompatibility?.isCompatible}
/>
<FreeRegisterSummary duration={duration} domain={domain} />
<Divider className="w-full" />
<TermCheckbox
checked={termsBox}
Expand All @@ -225,21 +210,27 @@ const FreeRegisterCheckout: FunctionComponent<FreeRegisterCheckoutProps> = ({
disabled={
(domainsMinting.get(encodedDomain) as boolean) ||
!account ||
!coupon ||
!duration ||
!targetAddress ||
!termsBox ||
Boolean(couponError) ||
loadingCoupon ||
loadingGas ||
loadingDeploymentData
loadingDeploymentData ||
loadingTypedData
}
>
{!termsBox
? "Please accept terms & policies"
: couponError
: couponError || !coupon
? "Enter a valid Coupon"
: loadingGas
? "Loading gas"
? invalidTx
? "Invalid signature"
: "Loading gas"
: loadingTypedData
? "Building typed data"
: loadingDeploymentData
? paymasterRewards.length > 0
? "Loading deployment data"
Expand Down
153 changes: 19 additions & 134 deletions components/discount/freeRegisterSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,23 @@ import React, { FunctionComponent } from "react";
import styles from "../../styles/components/registerV3.module.css";
import { getYearlyPrice } from "@/utils/priceService";
import DoneIcon from "../UI/iconsComponents/icons/doneIcon";
import { GasTokenPrice } from "@avnu/gasless-sdk";
import { tokenNames } from "@/utils/altcoinService";
import { shortenDomain } from "@/utils/stringService";
import StyledToolTip from "../UI/styledTooltip";
import { GasMethod } from "@/hooks/paymaster";
import { Alert } from "@mui/material";
import { useAccount } from "@starknet-react/core";

type FreeRegisterSummaryProps = {
duration: number;
domain: string;
hasPaymasterRewards?: boolean;
gasTokenPrices?: GasTokenPrice[];
gasTokenPrice?: GasTokenPrice;
setGasTokenPrice: (price: GasTokenPrice) => void;
gasMethod: GasMethod;
setGasMethod: (method: GasMethod) => void;
paymasterAvailable: boolean;
maxGasTokenAmount?: bigint;
deployed?: boolean;
};

const FreeRegisterSummary: FunctionComponent<FreeRegisterSummaryProps> = ({
domain,
duration,
hasPaymasterRewards,
gasTokenPrices,
gasTokenPrice,
setGasTokenPrice,
gasMethod,
setGasMethod,
paymasterAvailable,
maxGasTokenAmount,
deployed,
}) => {
const { address } = useAccount();

function getMessage() {
return `${Math.floor(duration / 30)} months of domain registration`;
}

const getTokenName = (price: GasTokenPrice) =>
tokenNames[price.tokenAddress as keyof typeof tokenNames] ||
shortenDomain(price.tokenAddress);

return (
<div className={styles.pricesSummary}>
<div className={styles.totalDue}>
Expand All @@ -56,112 +31,22 @@ const FreeRegisterSummary: FunctionComponent<FreeRegisterSummaryProps> = ({
<strong>Free</strong>
</p>
</div>
<div className={styles.gasMethods}>
<button
disabled={gasMethod === "traditional"}
onClick={() => setGasMethod("traditional")}
className={
gasMethod === "traditional"
? styles.gasMethodSelected
: styles.gasMethod
}
type="button"
>
Traditional Transaction
</button>
<StyledToolTip
title={`Allows you to pay less gas and choose other currencies to pay fees. ${
paymasterAvailable
? ""
: "Wallet not compatible. Please deploy it or switch to ArgentX in order to use Paymaster."
}`}
>
<button
onClick={() => setGasMethod("paymaster")}
className={
gasMethod === "paymaster"
? styles.gasMethodSelected
: styles.gasMethod
}
type="button"
disabled={!paymasterAvailable}
>
Gasless Transaction
</button>
</StyledToolTip>
</div>
{gasMethod === "paymaster" ? (
hasPaymasterRewards ? (
<div className="flex items-center gap-2">
<DoneIcon width="24" color="green" />
<p className="text-sm">
No gas fees to pay. You have a{" "}
<a
href="https://doc.avnu.fi/starknet-paymaster/introduction"
target="_blank"
rel="noreferrer noopener"
className="underline"
>
Paymaster
</a>{" "}
reward.
</p>
</div>
) : (
<div className="flex flex-col gap-2 w-full">
<p className="text-sm">
No{" "}
<a
href="https://doc.avnu.fi/starknet-paymaster/introduction"
target="_blank"
rel="noreferrer noopener"
className="underline"
>
Paymaster
</a>{" "}
reward. {deployed ? "Please select a gas token." : ""}
</p>
{deployed ? (
<>
<div className={styles.gasMethods}>
{gasTokenPrices?.map((price) => (
<button
disabled={
price.tokenAddress === gasTokenPrice?.tokenAddress
}
onClick={() => setGasTokenPrice(price)}
key={price.tokenAddress}
className={
price.tokenAddress === gasTokenPrice?.tokenAddress
? styles.gasMethodSelected
: styles.gasMethod
}
type="button"
>
{getTokenName(price)}{" "}
</button>
))}
</div>
{gasTokenPrice ? (
<Alert severity="info">
{maxGasTokenAmount
? `Please make sure to have at least ${maxGasTokenAmount.toString()} ${getTokenName(
gasTokenPrice
)} to prevent transaction failure.`
: `Please make sure to have enough ${getTokenName(
gasTokenPrice
)} to prevent transaction failure.`}
</Alert>
) : null}
</>
) : (
<Alert severity="error">
Your wallet is not deployed. To sponsor its deployment, please
gather rewards.
</Alert>
)}
</div>
)
{address ? (
<div className="flex items-center gap-2">
<DoneIcon width="24" color="green" />
<p className="text-sm">
No gas fees to pay. You have a{" "}
<a
href="https://doc.avnu.fi/starknet-paymaster/introduction"
target="_blank"
rel="noreferrer noopener"
className="underline"
>
Paymaster
</a>{" "}
reward.
</p>
</div>
) : null}
</div>
</div>
Expand Down
23 changes: 20 additions & 3 deletions hooks/isDeployed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,26 @@ export default function isStarknetDeployed(address?: string) {
const [isDeployed, setIsDeployed] = useState<boolean>(false);
const [deploymentData, setDeploymentData] =
useState<GetDeploymentDataResult>();
const [nonDeployedAddress, setNonDeployedAddress] = useState<string>();
const [reload, setReload] = useState<boolean>(false);

useEffect(() => {
if (!address || !provider || !connector?.id) return;
if (isDeployed || deploymentData) return;
const interval = setInterval(() => {
setReload(true);
}, 3000);
return () => clearInterval(interval);
}, [isDeployed, deploymentData]);

useEffect(() => {
if (reload) return setReload(false);
if (
!address ||
!provider ||
!connector?.id ||
address === nonDeployedAddress
)
return;
const checkIsDeployed = async () => {
try {
provider
Expand All @@ -33,7 +50,6 @@ export default function isStarknetDeployed(address?: string) {
setDeploymentData(undefined);
return;
}

availableWallets.forEach(async (connectedWallet) => {
if (
connectedWallet.id === connector?.id &&
Expand All @@ -47,6 +63,7 @@ export default function isStarknetDeployed(address?: string) {
);
if (isGetDeploymentDataResult(data)) {
setDeploymentData(data);
setNonDeployedAddress(address);
} else {
console.error(
"Received data is not in the expected format:",
Expand All @@ -63,7 +80,7 @@ export default function isStarknetDeployed(address?: string) {
};

checkIsDeployed();
}, [address, provider, connector?.id]);
}, [address, provider, connector?.id, nonDeployedAddress, reload]);

return { isDeployed, deploymentData };
}
Expand Down
Loading

0 comments on commit 8f3e3d2

Please sign in to comment.