Skip to content
This repository has been archived by the owner on Dec 21, 2023. It is now read-only.

Commit

Permalink
feat: pay with eth (#2480)
Browse files Browse the repository at this point in the history
* feat: buy with eth

* nit

* fix

* pay with eth fixes

* fix: display eth

* fix

* fix

* fix

* fix

* nits

* add eth balance

* nits

* default to eth
  • Loading branch information
intergalacticspacehighway authored Oct 31, 2023
1 parent b30cc66 commit d181c92
Show file tree
Hide file tree
Showing 11 changed files with 413 additions and 135 deletions.
1 change: 1 addition & 0 deletions apps/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"@types/react-datepicker": "^4.11.2",
"@types/react-dropzone": "^5.1.0",
"@types/react-stickynode": "^4.0.0",
"@uniswap/v3-periphery": "^1.4.4",
"@vercel/og": "^0.5.11",
"app": "*",
"array-to-image": "^1.0.0",
Expand Down
69 changes: 69 additions & 0 deletions packages/app/abi/CreatorTokenSwapRouterAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export const creatorTokenSwapRouterAbi = [
{
inputs: [
{ internalType: "address", name: "_universalRouter", type: "address" },
{ internalType: "address", name: "_wethAddress", type: "address" },
{ internalType: "address", name: "_usdcAddress", type: "address" },
],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "USDC_ADDRESS",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "WETH_ADDRESS",
outputs: [{ internalType: "address", name: "", type: "address" }],
stateMutability: "view",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "_creatorToken", type: "address" },
{ internalType: "uint256", name: "_numOfTokens", type: "uint256" },
{ internalType: "uint256", name: "_maxPayment", type: "uint256" },
],
name: "bulkBuyWithEth",
outputs: [{ internalType: "uint256", name: "_amountOut", type: "uint256" }],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "_creatorToken", type: "address" },
{ internalType: "address", name: "_to", type: "address" },
{ internalType: "uint256", name: "_numOfTokens", type: "uint256" },
{ internalType: "uint256", name: "_maxPayment", type: "uint256" },
],
name: "bulkBuyWithEth",
outputs: [{ internalType: "uint256", name: "_amountOut", type: "uint256" }],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "_creatorToken", type: "address" },
{ internalType: "address", name: "_to", type: "address" },
{ internalType: "uint256", name: "_maxPayment", type: "uint256" },
],
name: "buyWithEth",
outputs: [{ internalType: "uint256", name: "_amountOut", type: "uint256" }],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{ internalType: "address", name: "_creatorToken", type: "address" },
{ internalType: "uint256", name: "_maxPayment", type: "uint256" },
],
name: "buyWithEth",
outputs: [{ internalType: "uint256", name: "_amountOut", type: "uint256" }],
stateMutability: "payable",
type: "function",
},
];
107 changes: 88 additions & 19 deletions packages/app/components/creator-token/buy-creator-token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { Avatar } from "@showtime-xyz/universal.avatar";
import { BottomSheetModalProvider } from "@showtime-xyz/universal.bottom-sheet";
import { Button } from "@showtime-xyz/universal.button";
import { useIsDarkMode } from "@showtime-xyz/universal.hooks";
import { InformationCircle, LockBadge } from "@showtime-xyz/universal.icon";
import {
Ethereum,
InformationCircle,
LockBadge,
} from "@showtime-xyz/universal.icon";
import { Image } from "@showtime-xyz/universal.image";
import { ModalSheet } from "@showtime-xyz/universal.modal-sheet";
import { Pressable } from "@showtime-xyz/universal.pressable";
Expand All @@ -19,6 +23,7 @@ import { VerificationBadge } from "@showtime-xyz/universal.verification-badge";
import { View } from "@showtime-xyz/universal.view";

import { useUserProfile } from "app/hooks/api-hooks";
import { useGetETHForUSDC } from "app/hooks/auth/use-get-eth-for-usdc";
import { useContractBalanceOfToken } from "app/hooks/creator-token/use-balance-of-token";
import { useCreatorTokenBuy } from "app/hooks/creator-token/use-creator-token-buy";
import { useCreatorTokenPriceToBuyNext } from "app/hooks/creator-token/use-creator-token-price-to-buy-next";
Expand All @@ -27,6 +32,7 @@ import { useCreatorTokenSell } from "app/hooks/creator-token/use-creator-token-s
import { useWalletUSDCBalance } from "app/hooks/creator-token/use-wallet-usdc-balance";
import { useRedirectToCreatorTokensShare } from "app/hooks/use-redirect-to-creator-tokens-share-screen";
import { useWallet } from "app/hooks/use-wallet";
import { useWalletETHBalance } from "app/hooks/use-wallet-balance";

import { toast } from "design-system/toast";
import { Toggle } from "design-system/toggle";
Expand All @@ -44,6 +50,10 @@ const PAYMENT_METHODS = [
title: "USDC",
value: "USDC",
},
{
title: "ETH",
value: "ETH",
},
];
const SELECT_LIST = [
{
Expand All @@ -61,16 +71,22 @@ export const BuyCreatorToken = () => {
const [username] = useParam("username");
const [selectedActionParam] = useParam("selectedAction");
const [tokenAmount, setTokenAmount] = useState(1);
const [paymentMethod, setPaymentMethod] = useState("USDC");

const { data: profileData } = useUserProfile({ address: username });
const buyToken = useCreatorTokenBuy({ username, tokenAmount });
const sellToken = useCreatorTokenSell();
const redirectToCreatorTokensShare = useRedirectToCreatorTokensShare();

const [selectedAction, setSelectedAction] = useState<Query["selectedAction"]>(
selectedActionParam ?? "buy"
);

const [paymentMethod, setPaymentMethod] = useState<"ETH" | "USDC">(
selectedAction === "buy" ? "ETH" : "USDC"
);
const buyToken = useCreatorTokenBuy({ username, tokenAmount, paymentMethod });

const usdcBalance = useWalletUSDCBalance();
const ethBalance = useWalletETHBalance();
const [showExplanation, setShowExplanation] = useState(false);
const priceToBuyNext = useCreatorTokenPriceToBuyNext(
selectedAction === "buy"
Expand All @@ -80,6 +96,15 @@ export const BuyCreatorToken = () => {
}
: undefined
);

const ethPriceToBuyNext = useGetETHForUSDC(
paymentMethod === "ETH"
? {
amount: priceToBuyNext.data?.totalPrice,
}
: undefined
);

const priceToSellNext = useCreatorTokenPriceToSellNext(
selectedAction === "sell"
? {
Expand Down Expand Up @@ -123,7 +148,11 @@ export const BuyCreatorToken = () => {
: "Sell"}
</Button>
);
} else if (usdcBalance.data?.balance === 0n && !wallet.isMagicWallet) {
} else if (
paymentMethod === "USDC" &&
usdcBalance.data?.balance === 0n &&
!wallet.isMagicWallet
) {
return (
<Button
onPress={() =>
Expand All @@ -135,6 +164,18 @@ export const BuyCreatorToken = () => {
Buy USDC on Uniswap
</Button>
);
} else if (
paymentMethod === "ETH" &&
ethBalance.data?.balance === 0n &&
!wallet.isMagicWallet
) {
return (
<Button
onPress={() => Linking.openURL("https://bridge.base.org/deposit")}
>
Bridge ETH to Base
</Button>
);
} else {
return (
<Button
Expand All @@ -158,6 +199,8 @@ export const BuyCreatorToken = () => {
? "Please wait..."
: wallet.isMagicWallet
? "Connect"
: paymentMethod === "ETH"
? "Buy"
: "Approve & Buy"}
</Button>
);
Expand All @@ -171,6 +214,12 @@ export const BuyCreatorToken = () => {
setTokenAmount(1);
}
}, [selectedAction, tokenBalance.data]);

useEffect(() => {
if (selectedAction === "sell") {
setPaymentMethod("USDC");
}
}, [selectedAction, tokenBalance.data]);
const isDark = useIsDarkMode();

return (
Expand Down Expand Up @@ -206,9 +255,13 @@ export const BuyCreatorToken = () => {
<View tw="flex-1" style={{ rowGap: 16 }}>
<View tw="w-full flex-row items-center justify-between">
<Toggle
options={PAYMENT_METHODS}
options={
selectedAction === "buy"
? PAYMENT_METHODS
: PAYMENT_METHODS.slice(0, 1)
}
value={paymentMethod}
onChange={(value) => setPaymentMethod(value)}
onChange={(value: any) => setPaymentMethod(value)}
/>
<Pressable
onPress={() => {
Expand All @@ -223,21 +276,33 @@ export const BuyCreatorToken = () => {
</Pressable>
</View>
<View tw="flex-row items-center" style={{ columnGap: 4 }}>
<Image
source={{
uri: "https://media.showtime.xyz/assets/usdc%26base.png",
}}
width={44}
height={44}
tw="mr-2"
/>
{paymentMethod === "USDC" || selectedAction === "sell" ? (
<Image
source={{
uri: "https://media.showtime.xyz/assets/usdc%26base.png",
}}
width={44}
height={44}
tw="mr-2"
/>
) : (
<Ethereum
width={44}
height={44}
color={isDark ? "white" : "black"}
/>
)}
{selectedAction === "buy" ? (
<View>
{priceToBuyNext.isLoading ? (
{(priceToBuyNext.isLoading && paymentMethod === "USDC") ||
(ethPriceToBuyNext.isLoading &&
paymentMethod === "ETH") ? (
<Skeleton width={100} height={27} />
) : (
<Text tw="text-4xl font-semibold text-gray-800 dark:text-gray-200">
{priceToBuyNext.data?.displayPrice}
{paymentMethod === "USDC"
? priceToBuyNext.data?.displayPrice
: ethPriceToBuyNext.data?.displayValue}
</Text>
)}
</View>
Expand Down Expand Up @@ -327,16 +392,20 @@ export const BuyCreatorToken = () => {
<View tw="flex-row justify-between">
<Text tw="text-gray-700 dark:text-gray-200">
{selectedAction === "buy"
? "You will pay in USDC:"
? `You will pay in ${paymentMethod}:`
: "You will receive in USDC:"}
</Text>
{selectedAction === "buy" ? (
<>
{priceToBuyNext.isLoading ? (
{(priceToBuyNext.isLoading && paymentMethod === "USDC") ||
(ethPriceToBuyNext.isLoading && paymentMethod === "ETH") ? (
<Skeleton width={60} height={16} />
) : (
<Text tw="font-semibold text-gray-700 dark:text-gray-200">
${priceToBuyNext.data?.displayPrice}
$
{paymentMethod === "USDC"
? priceToBuyNext.data?.displayPrice
: ethPriceToBuyNext.data?.displayValue}
</Text>
)}
</>
Expand Down
50 changes: 50 additions & 0 deletions packages/app/hooks/auth/use-get-eth-for-usdc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import QuoterAbi from "@uniswap/v3-periphery/artifacts/contracts/lens/QuoterV2.sol/QuoterV2.json";
import useSWR from "swr";

import { Logger } from "app/lib/logger";
import { publicClient } from "app/lib/wallet-public-client";

import {
quoterv2Address,
usdcAddress,
wETHAddress,
} from "../creator-token/utils";

export const getETHForUSDC = (params?: { amount: bigint }) =>
params ? "getETHForUSDC" + params.amount : "";

export const useGetETHForUSDC = (params?: { amount: bigint }) => {
const res = useSWR(
getETHForUSDC(params),
async () => {
if (params?.amount) {
const req = {
tokenIn: wETHAddress,
tokenOut: usdcAddress,
fee: 500,
amount: params.amount + 500000n,
sqrtPriceLimitX96: 0,
};

const res: any = await publicClient.readContract({
address: quoterv2Address,
abi: QuoterAbi.abi,
functionName: "quoteExactOutputSingle",
args: [req],
});

console.log("res ", res);
return {
value: res[0],
displayValue: (Number(res[0]) / 10 ** 18).toFixed(6),
};
}
},
{
refreshInterval: 10_000,
revalidateOnMount: true,
}
);

return res;
};
Loading

0 comments on commit d181c92

Please sign in to comment.