diff --git a/src/ProtectedLayout.tsx b/src/ProtectedLayout.tsx index 09581e3b..630ae118 100644 --- a/src/ProtectedLayout.tsx +++ b/src/ProtectedLayout.tsx @@ -8,6 +8,7 @@ import Explorer from "./pages/Explorer"; import Field from "./pages/Field"; import { Market as MarketPage } from "./pages/Market"; import Overview from "./pages/Overview"; +import Referral from "./pages/Referral"; import Silo from "./pages/Silo"; import SiloToken from "./pages/SiloToken"; import Swap from "./pages/Swap"; @@ -60,6 +61,14 @@ export default function ProtectedLayout() { } /> + + + + } + /> void; +} + +export function DelegateReferralModal({ isOpen, onOpenChange }: DelegateReferralModalProps) { + const [delegateAddress, setDelegateAddress] = useState(""); + const [error, setError] = useState(""); + const chainId = useChainId(); + + // Check if address is valid + const isValidAddress = delegateAddress.trim() && isAddress(delegateAddress); + + // Transaction handling + const { writeWithEstimateGas, setSubmitting, isConfirming } = useTransaction({ + successCallback: () => { + setDelegateAddress(""); + onOpenChange(false); + }, + successMessage: "Delegate address updated successfully", + errorMessage: "Failed to update delegate address", + }); + + const handleSubmit = async () => { + // Validate address + if (!delegateAddress.trim()) { + setError("Please enter an address"); + return; + } + + if (!isAddress(delegateAddress)) { + setError("Invalid Ethereum address"); + return; + } + + setError(""); + + try { + setSubmitting(true); + toast.loading("Updating delegate address"); + + await writeWithEstimateGas({ + address: beanstalkAddress[chainId as keyof typeof beanstalkAddress], + abi: beanstalkAbi, + functionName: "delegateReferralRewards", + args: [delegateAddress as `0x${string}`], + }); + } catch (e) { + console.error("Failed to update delegate address:", e); + toast.dismiss(); + toast.error("Failed to update delegate address"); + } finally { + setSubmitting(false); + } + }; + + const handleReset = async () => { + setError(""); + + try { + setSubmitting(true); + toast.loading("Resetting delegate address..."); + + await writeWithEstimateGas({ + address: beanstalkAddress[chainId as keyof typeof beanstalkAddress], + abi: beanstalkAbi, + functionName: "delegateReferralRewards", + args: ["0x0000000000000000000000000000000000000000" as `0x${string}`], + }); + } catch (e) { + console.error("Failed to reset delegate address:", e); + toast.dismiss(); + toast.error("Failed to reset delegate address"); + } finally { + setSubmitting(false); + } + }; + + if (!isOpen) return null; + + return ( + +
+ {/* Title and separator */} +
+
+
📍 Change Pod Destination Address
+ +
+
+
+ + {/* Form Section */} + +
+ + { + setDelegateAddress(e.target.value); + setError(""); + }} + placeholder="0x..." + outlined + /> + {error && {error}} +
+ +
+ Enter the address where you want your referral reward Pods to be sent. +
+ + + {/* Action Buttons */} + + + + + +
+ + ); +} diff --git a/src/components/HowToCard.tsx b/src/components/HowToCard.tsx new file mode 100644 index 00000000..271de036 --- /dev/null +++ b/src/components/HowToCard.tsx @@ -0,0 +1,35 @@ +import { StepItem } from "./ui/StepItem"; + +const steps = [ + { + title: "Qualify as a Referrer", + description: "Sow at least 1,000 Pinto in the Field to unlock your referral link.", + }, + { + title: "Share Your Link", + description: "Copy your unique referral link and share it with friends, on social media, or anywhere else.", + }, + { + title: "Earn Rewards", + description: + "When someone uses your link and Sows Pinto, you earn 10% of the Pods they receive as a referral bonus.", + }, + { + title: "Get Credited", + description: + "Referral rewards are automatically credited to your wallet address when your referral completes their Sow transaction.", + }, +]; + +export function HowToCard() { + return ( +
+
How It Works
+
+ {steps.map((step, index) => ( + + ))} +
+
+ ); +} diff --git a/src/components/ReferralLeaderboard.tsx b/src/components/ReferralLeaderboard.tsx new file mode 100644 index 00000000..9eba3c65 --- /dev/null +++ b/src/components/ReferralLeaderboard.tsx @@ -0,0 +1,264 @@ +import podIcon from "@/assets/protocol/Pod.png"; +import FrameAnimator from "@/components/LoadingSpinner"; +import { Button } from "@/components/ui/Button"; +import { Card, CardContent, CardHeader } from "@/components/ui/Card"; +import IconImage from "@/components/ui/IconImage"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/Table"; +import { useReferralLeaderboard } from "@/state/referral"; +import { formatter, truncateHex } from "@/utils/format"; +import { cn } from "@/utils/utils"; +import { useCallback, useMemo, useState } from "react"; +import { useAccount } from "wagmi"; + +/** + * Shows top referrers ranked by Pods earned in a paginated table. + */ +export default function ReferralLeaderboard() { + // Local state for client-side pagination + const { address: walletAddress } = useAccount(); + const [displayStartIndex, setDisplayStartIndex] = useState(0); + const [isRetrying, setIsRetrying] = useState(false); + const rowsPerPage = 25; // Number of entries to display per page + + // Fetch all leaderboard data using optimized hook + const { data: allData, isLoading, error, refetch, userRank } = useReferralLeaderboard(); + + // Memoized pagination calculations to prevent unnecessary re-renders + const paginationState = useMemo(() => { + const totalEntries = allData.length; + const totalPages = Math.ceil(totalEntries / rowsPerPage); + const currentPage = Math.floor(displayStartIndex / rowsPerPage) + 1; + const endIndex = Math.min(displayStartIndex + rowsPerPage, totalEntries); + const displayData = allData.slice(displayStartIndex, endIndex); + + // Determine if navigation buttons should be enabled + const canGoNext = endIndex < totalEntries; + const canGoPrevious = displayStartIndex > 0; + + return { + displayData, + currentPage, + totalPages, + totalEntries, + canGoNext, + canGoPrevious, + startIndex: displayStartIndex, + endIndex, + }; + }, [allData, displayStartIndex, rowsPerPage]); + + // Memoized pagination handlers to prevent unnecessary re-renders + const handleNextPage = useCallback(() => { + if (paginationState.canGoNext && !isLoading && !isRetrying) { + setDisplayStartIndex((prev) => prev + rowsPerPage); + } + }, [paginationState.canGoNext, isLoading, isRetrying, rowsPerPage]); + + const handlePreviousPage = useCallback(() => { + if (paginationState.canGoPrevious && !isLoading && !isRetrying) { + setDisplayStartIndex((prev) => Math.max(0, prev - rowsPerPage)); + } + }, [paginationState.canGoPrevious, isLoading, isRetrying, rowsPerPage]); + + // Memoized retry handler for failed requests + const handleRetry = useCallback(async () => { + setIsRetrying(true); + try { + await refetch(); + } catch (err) { + console.error("Retry failed:", err); + } finally { + setIsRetrying(false); + } + }, [refetch]); + + // Memoized error state classification to prevent unnecessary re-renders + const errorState = useMemo(() => { + if (!error) return null; + + const isNetworkError = + error.message.includes("fetch") || error.message.includes("network") || error.message.includes("timeout"); + const isBlockHeightError = error.message.includes("block"); + + let message: string; + if (isNetworkError) { + message = "Network error occurred while loading referral data."; + } else if (isBlockHeightError) { + message = "Block height error occurred. Data may be temporarily unavailable."; + } else { + message = "Failed to load referral data."; + } + + const suggestion = isNetworkError ? "Please check your connection and try again." : "Please try again later."; + + return { + isNetworkError, + isBlockHeightError, + message, + suggestion, + }; + }, [error]); + + // Initial loading state (first page load) + if (isLoading && allData.length === 0) { + return ( + + +
Referral Leaderboard
+

Top referrers ranked by Pods earned

+
+ +
+ +
+
+
+ ); + } + + // Error state with retry functionality + if (error && allData.length === 0 && errorState) { + return ( + + +
Referral Leaderboard
+

Error loading leaderboard data

+
+ +
+
+
{errorState.message}
+
{errorState.suggestion}
+
+ +
+
+
+ ); + } + + // Empty state + if (!allData || allData.length === 0) { + return ( + + +
Referral Leaderboard
+

Top referrers ranked by Pods earned

+
+ + + + + No. + Address + Pods Earned + Referrals + + + + + +
+ No referral activity yet. Be the first to earn referral rewards! +
+
+
+
+
+
+
+ ); + } + + // Main table with data + return ( + + +
+
Referral Leaderboard
+ {(isLoading || isRetrying) && } +
+

Top referrers ranked by Pods earned

+ {error && allData.length > 0 && ( +
+
+ Warning: Some data may be outdated due to loading errors. + +
+
+ )} +
+ + + + + No. + Address + Pods Earned + Referrals + + + + {paginationState.displayData?.map((entry) => ( + + {entry.rank} + + {truncateHex(entry.address, 6, 4)} + + +
+ + {formatter.noDec(entry.podsEarned)} +
+
+ {entry.totalSuccessfulReferrals} +
+ ))} +
+
+ + {/* Pagination controls matching MarketActivityTable.tsx */} + {paginationState.totalPages > 1 && ( +
+ +
{`${paginationState.currentPage} of ${paginationState.totalPages}`}
+ +
+ )} +
+
+ ); +} diff --git a/src/components/ReferralLinkGenerator.tsx b/src/components/ReferralLinkGenerator.tsx new file mode 100644 index 00000000..ff7a7ce2 --- /dev/null +++ b/src/components/ReferralLinkGenerator.tsx @@ -0,0 +1,196 @@ +import telegramLogo from "@/assets/misc/telegram-logo.png"; +import xLogo from "@/assets/misc/x-logo.png"; +import { SowRequirementCard } from "@/components/SowRequirementCard"; +import { Button } from "@/components/ui/Button"; +import { Input } from "@/components/ui/Input"; +import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; +import { useFarmerSowEligibility } from "@/hooks/useFarmerSowEligibility"; +import { useReferralData } from "@/state/referral"; +import { trackSimpleEvent } from "@/utils/analytics"; +import { truncateHex } from "@/utils/format"; +import { encodeReferralAddress } from "@/utils/referral"; +import { CopyIcon } from "@radix-ui/react-icons"; +import { toast } from "sonner"; +import { useAccount } from "wagmi"; + +interface ReferralLinkGeneratorProps { + onChangeAddress: () => void; +} + +export function ReferralLinkGenerator({ onChangeAddress }: ReferralLinkGeneratorProps) { + const { address } = useAccount(); + const { delegateAddress } = useReferralData(); + const { meetsRequirement, totalPods, amountNeeded, progressPercentage } = useFarmerSowEligibility(); + + const isWalletConnected = !!address; + + const referralCode = address ? encodeReferralAddress(address) : ""; + const referralUrl = `${window.location.origin}/field?ref=${referralCode}`; + const podDestinationAddress = delegateAddress || address; + + const handleCopyCode = () => { + if (!isWalletConnected) return; + + navigator.clipboard.writeText(referralCode); + toast.success("Referral code copied to clipboard!"); + + trackSimpleEvent(ANALYTICS_EVENTS.REFERRAL.LINK_COPIED, { + address, + type: "code", + }); + }; + + const handleCopyLink = () => { + if (!isWalletConnected) return; + + navigator.clipboard.writeText(referralUrl); + toast.success("Referral link copied to clipboard!"); + + trackSimpleEvent(ANALYTICS_EVENTS.REFERRAL.LINK_COPIED, { + address, + type: "link", + }); + }; + + const handleTwitterShare = () => { + if (!isWalletConnected) return; + + console.log("Twitter/X share clicked"); + const tweetText = + "🌱 I'm farming on @PintoProtocol and earning passive rewards!\n\nJoin me and I'll earn bonus Pods when you Sow Pinto 🫘\n\nStart farming today:"; + const twitterUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(referralUrl)}`; + window.open(twitterUrl, "_blank", "noopener,noreferrer"); + + trackSimpleEvent(ANALYTICS_EVENTS.REFERRAL.TWITTER_SHARE, { + address, + }); + }; + + const handleTelegramShare = () => { + if (!isWalletConnected) return; + + console.log("Telegram share clicked"); + const shareText = + "🌱 I'm farming on Pinto Protocol and earning passive rewards! Join me and I'll earn bonus Pods when you Sow Pinto 🫘 Start farming today"; + const telegramUrl = `https://t.me/share/url?url=${encodeURIComponent(referralUrl)}&text=${encodeURIComponent(shareText)}`; + window.open(telegramUrl, "_blank", "noopener,noreferrer"); + + trackSimpleEvent(ANALYTICS_EVENTS.REFERRAL.LINK_SHARED, { + address, + platform: "telegram", + }); + }; + + return ( +
+
Invite via
+ +
+ {/* Referral Code */} +
+ +
+ + +
+
+ + {/* Referral Link */} +
+ +
+ + +
+
+ +
+ {" "} + {/* Conditional rendering based on sow requirement */} + {!meetsRequirement || !isWalletConnected ? ( + + ) : ( + /* Pod Destination Address and Share via - Row Layout */ +
+ {/* Pod Destination Address */} +
+ +
+ + {podDestinationAddress ? truncateHex(podDestinationAddress, 6, 4) : "-"} + + +
+
+
+ )} + {/* Social Sharing Icons */} +
+ +
+ + +
+
+
+
+
+ ); +} diff --git a/src/components/ReferralStatsCard.tsx b/src/components/ReferralStatsCard.tsx new file mode 100644 index 00000000..ce85420b --- /dev/null +++ b/src/components/ReferralStatsCard.tsx @@ -0,0 +1,51 @@ +import FrameAnimator from "@/components/LoadingSpinner"; +import { useFarmerReferralData, useReferralLeaderboard } from "@/state/referral"; +import { formatter } from "@/utils/format"; + +export function ReferralStatsCard() { + const { data, isLoading } = useFarmerReferralData(); + const { data: leaderboardData, userRank, isLoading: isLeaderboardLoading } = useReferralLeaderboard(); + + if (isLoading || isLeaderboardLoading) { + return ( +
+
Your Referral Stats
+
+ +
+
+ ); + } + + const statsData = [ + { + label: "Total Pods Earned", + value: data ? formatter.noDec(data.totalPodsEarned) : "0", + description: "Pods earned from referrals", + }, + { + label: "Total successful referrals", + value: data?.refereeCount ?? 0, + description: "Number of users who used your link", + }, + { + label: "Referral Ranking", + value: userRank && leaderboardData ? `#${userRank}/${leaderboardData.length}` : "-", + description: "Your rank among all referrers", + }, + ]; + + return ( +
+
Your Referral Stats
+
+ {statsData.map((stat) => ( +
+
{stat.label}
+
{stat.value}
+
+ ))} +
+
+ ); +} diff --git a/src/components/SowRequirementCard.tsx b/src/components/SowRequirementCard.tsx new file mode 100644 index 00000000..8c9388a2 --- /dev/null +++ b/src/components/SowRequirementCard.tsx @@ -0,0 +1,48 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { Progress } from "@/components/ui/Progress"; +import { formatter } from "@/utils/format"; + +interface SowRequirementCardProps { + totalPods: TokenValue; + amountNeeded: TokenValue; + progressPercentage: number; + disabled?: boolean; +} + +export function SowRequirementCard({ + totalPods, + amountNeeded, + progressPercentage, + disabled = false, +}: SowRequirementCardProps) { + return ( +
+
+
+
+ {disabled + ? "Connect your wallet to access referral features" + : "You currently do not meet the criteria to refer farmers"} +
+
+ +
+
+ Progress + + {disabled ? "- / 1000 Pinto sown" : `${formatter.number(totalPods)} / 1000 Pinto sown`} + +
+ + {!disabled && !amountNeeded.isZero && ( +
{formatter.number(amountNeeded)} more Pinto needed
+ )} + {disabled &&
Connect wallet to view progress
} +
+
+
+ ); +} diff --git a/src/components/nav/nav/Navbar.tsx b/src/components/nav/nav/Navbar.tsx index 12919d3a..d9d31b8e 100644 --- a/src/components/nav/nav/Navbar.tsx +++ b/src/components/nav/nav/Navbar.tsx @@ -331,6 +331,7 @@ export const navLinks = { silo: "/silo", field: "/field", swap: "/swap", + referral: "/referral", sPinto: "/sPinto", collection: "/collection", podmarket: "/market/pods", diff --git a/src/components/nav/nav/Navi.desktop.tsx b/src/components/nav/nav/Navi.desktop.tsx index c4ffabfc..da6f6eec 100644 --- a/src/components/nav/nav/Navi.desktop.tsx +++ b/src/components/nav/nav/Navi.desktop.tsx @@ -105,6 +105,11 @@ const AppNavi = () => { Pod Market + + + Referral + + sPinto diff --git a/src/components/nav/nav/Navi.mobile.tsx b/src/components/nav/nav/Navi.mobile.tsx index 2dd4a2c1..7581cce5 100644 --- a/src/components/nav/nav/Navi.mobile.tsx +++ b/src/components/nav/nav/Navi.mobile.tsx @@ -168,6 +168,13 @@ function MobileNavContent({ learnOpen, setLearnOpen, unmount, close }: IMobileNa > Swap + + Referral + +
+ {stepNumber} +
+
+
{title}
+
{description}
+
+
+ ); +} diff --git a/src/constants/abi/diamondABI.ts b/src/constants/abi/diamondABI.ts index aea43313..dc0414c6 100644 --- a/src/constants/abi/diamondABI.ts +++ b/src/constants/abi/diamondABI.ts @@ -10732,4 +10732,205 @@ export const diamondABI = [ stateMutability: "view", type: "function", }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "referrer", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "referrerIndex", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "referrerPods", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "referee", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "refereeIndex", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "refereePods", + type: "uint256", + }, + ], + name: "SowReferral", + type: "event", + }, + { + inputs: [ + { + internalType: "address", + name: "delegate", + type: "address", + }, + ], + name: "delegateReferralRewards", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "getBeanSownEligibilityThreshold", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "referrer", + type: "address", + }, + ], + name: "getBeansSownForReferral", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "referrer", + type: "address", + }, + ], + name: "getDelegate", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getRefereePercentage", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getReferrerPercentage", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "referrer", + type: "address", + }, + ], + name: "isValidReferrer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "beans", + type: "uint256", + }, + { + internalType: "uint256", + name: "minTemperature", + type: "uint256", + }, + { + internalType: "uint256", + name: "minSoil", + type: "uint256", + }, + { + internalType: "enum LibTransfer.From", + name: "mode", + type: "uint8", + }, + { + internalType: "address", + name: "referral", + type: "address", + }, + ], + name: "sowWithReferral", + outputs: [ + { + internalType: "uint256", + name: "pods", + type: "uint256", + }, + { + internalType: "uint256", + name: "referrerPods", + type: "uint256", + }, + { + internalType: "uint256", + name: "refereePods", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, ] as const; diff --git a/src/constants/analytics-events.ts b/src/constants/analytics-events.ts index 20fecd63..bf5db581 100644 --- a/src/constants/analytics-events.ts +++ b/src/constants/analytics-events.ts @@ -18,6 +18,7 @@ const NAVIGATION_EVENTS = { MAIN_SILO_CLICK: "navigation_main_silo_click", MAIN_FIELD_CLICK: "navigation_main_field_click", MAIN_SWAP_CLICK: "navigation_main_swap_click", + MAIN_REFERRAL_CLICK: "navigation_main_referral_click", MAIN_PODMARKET_CLICK: "navigation_main_podmarket_click", MAIN_SPINTO_CLICK: "navigation_main_spinto_click", MAIN_COLLECTION_CLICK: "navigation_main_collection_click", @@ -148,6 +149,26 @@ const SILO_EVENTS = { WRAPPED_TOKEN_SPECTRA_YIELD_TRADING_CLICK: "silo_wrapped_token_spectra_yield_trading_click", } as const; +// Referral Events +const REFERRAL_EVENTS = { + // Referral Tab & Page + TAB_OPENED: "referral_tab_opened", + PAGE_VIEWED: "referral_page_viewed", + + // Link Generation & Sharing + LINK_GENERATED: "referral_link_generated", + LINK_COPIED: "referral_link_copied", + LINK_SHARED: "referral_link_shared", + TWITTER_SHARE: "referral_twitter_share", + + // Validation + ELIGIBILITY_CHECKED: "referral_eligibility_checked", + VALIDATION_FAILED: "referral_validation_failed", + + // Transaction Events + SOW_WITH_REFERRAL: "referral_sow_executed", +} as const; + // Field Events const FIELD_EVENTS = { // Field Page Navigation @@ -420,6 +441,7 @@ export const ANALYTICS_EVENTS = { WALLET: WALLET_EVENTS, SILO: SILO_EVENTS, FIELD: FIELD_EVENTS, + REFERRAL: REFERRAL_EVENTS, SWAP: SWAP_EVENTS, MARKET: MARKET_EVENTS, COLLECTION: COLLECTION_EVENTS, @@ -438,6 +460,7 @@ export type AnalyticsEventName = | (typeof WALLET_EVENTS)[keyof typeof WALLET_EVENTS] | (typeof SILO_EVENTS)[keyof typeof SILO_EVENTS] | (typeof FIELD_EVENTS)[keyof typeof FIELD_EVENTS] + | (typeof REFERRAL_EVENTS)[keyof typeof REFERRAL_EVENTS] | (typeof SWAP_EVENTS)[keyof typeof SWAP_EVENTS] | (typeof MARKET_EVENTS)[keyof typeof MARKET_EVENTS] | (typeof COLLECTION_EVENTS)[keyof typeof COLLECTION_EVENTS] diff --git a/src/constants/meta.ts b/src/constants/meta.ts index db4a6cf8..4986a172 100644 --- a/src/constants/meta.ts +++ b/src/constants/meta.ts @@ -25,6 +25,7 @@ const slugs = [ "PINTO", "sPinto", "tractor", + "referral", ] as const; const nestedSlugs = ["PINTOUSDC", "PINTOcbBTC", "PINTOWSOL", "PINTOWETH", "PINTOcbETH", "PINTO"] as const; @@ -133,6 +134,11 @@ const PINTO_META: Record = { description: "View and manage your collection of Pinto NFTs.", url: "https://pinto.money/collectionsoon", }, + referral: { + title: "Referral | Pinto", + description: "Share Pinto and earn rewards through referrals.", + url: "https://pinto.money/referral", + }, }; export default PINTO_META; diff --git a/src/generated/contractHooks.ts b/src/generated/contractHooks.ts index 6b1086fd..26efd600 100644 --- a/src/generated/contractHooks.ts +++ b/src/generated/contractHooks.ts @@ -6105,6 +6105,115 @@ export const beanstalkAbi = [ outputs: [{ name: '', internalType: 'string', type: 'string' }], stateMutability: 'view', }, + { + type: 'event', + anonymous: false, + inputs: [ + { + name: 'referrer', + internalType: 'address', + type: 'address', + indexed: true, + }, + { + name: 'referrerIndex', + internalType: 'uint256', + type: 'uint256', + indexed: false, + }, + { + name: 'referrerPods', + internalType: 'uint256', + type: 'uint256', + indexed: false, + }, + { + name: 'referee', + internalType: 'address', + type: 'address', + indexed: true, + }, + { + name: 'refereeIndex', + internalType: 'uint256', + type: 'uint256', + indexed: false, + }, + { + name: 'refereePods', + internalType: 'uint256', + type: 'uint256', + indexed: false, + }, + ], + name: 'SowReferral', + }, + { + type: 'function', + inputs: [{ name: 'delegate', internalType: 'address', type: 'address' }], + name: 'delegateReferralRewards', + outputs: [], + stateMutability: 'nonpayable', + }, + { + type: 'function', + inputs: [], + name: 'getBeanSownEligibilityThreshold', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [{ name: 'referrer', internalType: 'address', type: 'address' }], + name: 'getBeansSownForReferral', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [{ name: 'referrer', internalType: 'address', type: 'address' }], + name: 'getDelegate', + outputs: [{ name: '', internalType: 'address', type: 'address' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'getRefereePercentage', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [], + name: 'getReferrerPercentage', + outputs: [{ name: '', internalType: 'uint256', type: 'uint256' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [{ name: 'referrer', internalType: 'address', type: 'address' }], + name: 'isValidReferrer', + outputs: [{ name: '', internalType: 'bool', type: 'bool' }], + stateMutability: 'view', + }, + { + type: 'function', + inputs: [ + { name: 'beans', internalType: 'uint256', type: 'uint256' }, + { name: 'minTemperature', internalType: 'uint256', type: 'uint256' }, + { name: 'minSoil', internalType: 'uint256', type: 'uint256' }, + { name: 'mode', internalType: 'enum LibTransfer.From', type: 'uint8' }, + { name: 'referral', internalType: 'address', type: 'address' }, + ], + name: 'sowWithReferral', + outputs: [ + { name: 'pods', internalType: 'uint256', type: 'uint256' }, + { name: 'referrerPods', internalType: 'uint256', type: 'uint256' }, + { name: 'refereePods', internalType: 'uint256', type: 'uint256' }, + ], + stateMutability: 'nonpayable', + }, ] as const /** @@ -13199,6 +13308,99 @@ export const useReadBeanstalk_Uri = /*#__PURE__*/ createUseReadContract({ functionName: 'uri', }) +/** + * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"getBeanSownEligibilityThreshold"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useReadBeanstalk_GetBeanSownEligibilityThreshold = + /*#__PURE__*/ createUseReadContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'getBeanSownEligibilityThreshold', + }) + +/** + * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"getBeansSownForReferral"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useReadBeanstalk_GetBeansSownForReferral = + /*#__PURE__*/ createUseReadContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'getBeansSownForReferral', + }) + +/** + * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"getDelegate"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useReadBeanstalk_GetDelegate = /*#__PURE__*/ createUseReadContract( + { abi: beanstalkAbi, address: beanstalkAddress, functionName: 'getDelegate' }, +) + +/** + * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"getRefereePercentage"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useReadBeanstalk_GetRefereePercentage = + /*#__PURE__*/ createUseReadContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'getRefereePercentage', + }) + +/** + * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"getReferrerPercentage"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useReadBeanstalk_GetReferrerPercentage = + /*#__PURE__*/ createUseReadContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'getReferrerPercentage', + }) + +/** + * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"isValidReferrer"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useReadBeanstalk_IsValidReferrer = + /*#__PURE__*/ createUseReadContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'isValidReferrer', + }) + /** * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ * @@ -14403,6 +14605,38 @@ export const useWriteBeanstalk_Sunrise = /*#__PURE__*/ createUseWriteContract({ functionName: 'sunrise', }) +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"delegateReferralRewards"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_DelegateReferralRewards = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'delegateReferralRewards', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"sowWithReferral"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_SowWithReferral = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'sowWithReferral', + }) + /** * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ * @@ -15627,6 +15861,38 @@ export const useSimulateBeanstalk_Sunrise = functionName: 'sunrise', }) +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"delegateReferralRewards"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_DelegateReferralRewards = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'delegateReferralRewards', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"sowWithReferral"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_SowWithReferral = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'sowWithReferral', + }) + /** * Wraps __{@link useWatchContractEvent}__ with `abi` set to __{@link beanstalkAbi}__ * @@ -16805,6 +17071,22 @@ export const useWatchBeanstalk_Uri = /*#__PURE__*/ createUseWatchContractEvent({ eventName: 'URI', }) +/** + * Wraps __{@link useWatchContractEvent}__ with `abi` set to __{@link beanstalkAbi}__ and `eventName` set to `"SowReferral"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWatchBeanstalk_SowReferral = + /*#__PURE__*/ createUseWatchContractEvent({ + abi: beanstalkAbi, + address: beanstalkAddress, + eventName: 'SowReferral', + }) + /** * Wraps __{@link useReadContract}__ with `abi` set to __{@link beanstalkPriceAbi}__ * diff --git a/src/generated/gql/pintostalk/gql.ts b/src/generated/gql/pintostalk/gql.ts index 85c8092f..bd81f722 100644 --- a/src/generated/gql/pintostalk/gql.ts +++ b/src/generated/gql/pintostalk/gql.ts @@ -29,6 +29,8 @@ type Documents = { "fragment PodFill on PodFill {\n id\n placeInLine\n amount\n index\n start\n costInBeans\n fromFarmer {\n id\n }\n toFarmer {\n id\n }\n listing {\n id\n originalAmount\n }\n order {\n id\n beanAmount\n }\n createdAt\n}": typeof types.PodFillFragmentDoc, "fragment PodListing on PodListing {\n id\n farmer {\n id\n }\n historyID\n index\n start\n mode\n pricingType\n pricePerPod\n pricingFunction\n maxHarvestableIndex\n minFillAmount\n originalIndex\n originalPlaceInLine\n originalAmount\n filled\n amount\n remainingAmount\n filledAmount\n fill {\n placeInLine\n }\n status\n createdAt\n updatedAt\n creationHash\n}": typeof types.PodListingFragmentDoc, "fragment PodOrder on PodOrder {\n id\n farmer {\n id\n }\n historyID\n pricingType\n pricePerPod\n pricingFunction\n maxPlaceInLine\n minFillAmount\n beanAmount\n podAmountFilled\n beanAmountFilled\n status\n createdAt\n updatedAt\n creationHash\n}": typeof types.PodOrderFragmentDoc, + "query FarmerReferral($id: ID!) {\n farmer(id: $id) {\n id\n totalReferralRewardPodsReceived\n refereeCount\n }\n}": typeof types.FarmerReferralDocument, + "query ReferralLeaderboard($first: Int!, $skip: Int!, $block: Block_height) {\n farmers(\n first: $first\n skip: $skip\n orderBy: totalReferralRewardPodsReceived\n orderDirection: desc\n where: {refereeCount_gte: 0}\n block: $block\n ) {\n id\n refereeCount\n totalReferralRewardPodsReceived\n }\n}": typeof types.ReferralLeaderboardDocument, "query FarmerSeasonalSilo($from: Int, $to: Int, $account: String) {\n siloHourlySnapshots(\n where: {silo: $account, season_gte: $from, season_lte: $to}\n first: 1000\n orderBy: season\n orderDirection: asc\n ) {\n id\n season\n createdAt\n plantedBeans\n stalk\n germinatingStalk\n depositedBDV\n }\n}": typeof types.FarmerSeasonalSiloDocument, "query FarmerSeasonalSiloAssetToken($from: Int, $to: Int, $siloAsset: String) {\n siloAssetHourlySnapshots(\n where: {siloAsset: $siloAsset, season_gte: $from, season_lte: $to}\n first: 1000\n orderBy: season\n orderDirection: asc\n ) {\n id\n season\n depositedAmount\n depositedBDV\n deltaDepositedBDV\n deltaDepositedAmount\n createdAt\n }\n}": typeof types.FarmerSeasonalSiloAssetTokenDocument, "query BeanstalkSeasonalSiloActiveFarmers($from: Int, $to: Int, $silo: String) {\n siloHourlySnapshots(\n where: {season_gte: $from, season_lte: $to, silo: $silo, stalk_gt: 0}\n first: 1000\n orderBy: season\n orderDirection: desc\n ) {\n id\n season\n activeFarmers\n }\n}": typeof types.BeanstalkSeasonalSiloActiveFarmersDocument, @@ -55,6 +57,8 @@ const documents: Documents = { "fragment PodFill on PodFill {\n id\n placeInLine\n amount\n index\n start\n costInBeans\n fromFarmer {\n id\n }\n toFarmer {\n id\n }\n listing {\n id\n originalAmount\n }\n order {\n id\n beanAmount\n }\n createdAt\n}": types.PodFillFragmentDoc, "fragment PodListing on PodListing {\n id\n farmer {\n id\n }\n historyID\n index\n start\n mode\n pricingType\n pricePerPod\n pricingFunction\n maxHarvestableIndex\n minFillAmount\n originalIndex\n originalPlaceInLine\n originalAmount\n filled\n amount\n remainingAmount\n filledAmount\n fill {\n placeInLine\n }\n status\n createdAt\n updatedAt\n creationHash\n}": types.PodListingFragmentDoc, "fragment PodOrder on PodOrder {\n id\n farmer {\n id\n }\n historyID\n pricingType\n pricePerPod\n pricingFunction\n maxPlaceInLine\n minFillAmount\n beanAmount\n podAmountFilled\n beanAmountFilled\n status\n createdAt\n updatedAt\n creationHash\n}": types.PodOrderFragmentDoc, + "query FarmerReferral($id: ID!) {\n farmer(id: $id) {\n id\n totalReferralRewardPodsReceived\n refereeCount\n }\n}": types.FarmerReferralDocument, + "query ReferralLeaderboard($first: Int!, $skip: Int!, $block: Block_height) {\n farmers(\n first: $first\n skip: $skip\n orderBy: totalReferralRewardPodsReceived\n orderDirection: desc\n where: {refereeCount_gte: 0}\n block: $block\n ) {\n id\n refereeCount\n totalReferralRewardPodsReceived\n }\n}": types.ReferralLeaderboardDocument, "query FarmerSeasonalSilo($from: Int, $to: Int, $account: String) {\n siloHourlySnapshots(\n where: {silo: $account, season_gte: $from, season_lte: $to}\n first: 1000\n orderBy: season\n orderDirection: asc\n ) {\n id\n season\n createdAt\n plantedBeans\n stalk\n germinatingStalk\n depositedBDV\n }\n}": types.FarmerSeasonalSiloDocument, "query FarmerSeasonalSiloAssetToken($from: Int, $to: Int, $siloAsset: String) {\n siloAssetHourlySnapshots(\n where: {siloAsset: $siloAsset, season_gte: $from, season_lte: $to}\n first: 1000\n orderBy: season\n orderDirection: asc\n ) {\n id\n season\n depositedAmount\n depositedBDV\n deltaDepositedBDV\n deltaDepositedAmount\n createdAt\n }\n}": types.FarmerSeasonalSiloAssetTokenDocument, "query BeanstalkSeasonalSiloActiveFarmers($from: Int, $to: Int, $silo: String) {\n siloHourlySnapshots(\n where: {season_gte: $from, season_lte: $to, silo: $silo, stalk_gt: 0}\n first: 1000\n orderBy: season\n orderDirection: desc\n ) {\n id\n season\n activeFarmers\n }\n}": types.BeanstalkSeasonalSiloActiveFarmersDocument, @@ -140,6 +144,14 @@ export function graphql(source: "fragment PodListing on PodListing {\n id\n fa * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "fragment PodOrder on PodOrder {\n id\n farmer {\n id\n }\n historyID\n pricingType\n pricePerPod\n pricingFunction\n maxPlaceInLine\n minFillAmount\n beanAmount\n podAmountFilled\n beanAmountFilled\n status\n createdAt\n updatedAt\n creationHash\n}"): (typeof documents)["fragment PodOrder on PodOrder {\n id\n farmer {\n id\n }\n historyID\n pricingType\n pricePerPod\n pricingFunction\n maxPlaceInLine\n minFillAmount\n beanAmount\n podAmountFilled\n beanAmountFilled\n status\n createdAt\n updatedAt\n creationHash\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query FarmerReferral($id: ID!) {\n farmer(id: $id) {\n id\n totalReferralRewardPodsReceived\n refereeCount\n }\n}"): (typeof documents)["query FarmerReferral($id: ID!) {\n farmer(id: $id) {\n id\n totalReferralRewardPodsReceived\n refereeCount\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query ReferralLeaderboard($first: Int!, $skip: Int!, $block: Block_height) {\n farmers(\n first: $first\n skip: $skip\n orderBy: totalReferralRewardPodsReceived\n orderDirection: desc\n where: {refereeCount_gte: 0}\n block: $block\n ) {\n id\n refereeCount\n totalReferralRewardPodsReceived\n }\n}"): (typeof documents)["query ReferralLeaderboard($first: Int!, $skip: Int!, $block: Block_height) {\n farmers(\n first: $first\n skip: $skip\n orderBy: totalReferralRewardPodsReceived\n orderDirection: desc\n where: {refereeCount_gte: 0}\n block: $block\n ) {\n id\n refereeCount\n totalReferralRewardPodsReceived\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/src/generated/gql/pintostalk/graphql.ts b/src/generated/gql/pintostalk/graphql.ts index 1e48ae4d..e1a3f1c1 100644 --- a/src/generated/gql/pintostalk/graphql.ts +++ b/src/generated/gql/pintostalk/graphql.ts @@ -14097,6 +14097,22 @@ export type PodListingFragment = { __typename?: 'PodListing', id: string, histor export type PodOrderFragment = { __typename?: 'PodOrder', id: string, historyID: string, pricingType?: number | null, pricePerPod: number, pricingFunction?: any | null, maxPlaceInLine: any, minFillAmount: any, beanAmount: any, podAmountFilled: any, beanAmountFilled: any, status: MarketStatus, createdAt: any, updatedAt: any, creationHash: any, farmer: { __typename?: 'Farmer', id: any } }; +export type FarmerReferralQueryVariables = Exact<{ + id: Scalars['ID']['input']; +}>; + + +export type FarmerReferralQuery = { __typename?: 'Query', farmer?: { __typename?: 'Farmer', id: any, totalReferralRewardPodsReceived: any, refereeCount: number } | null }; + +export type ReferralLeaderboardQueryVariables = Exact<{ + first: Scalars['Int']['input']; + skip: Scalars['Int']['input']; + block?: InputMaybe; +}>; + + +export type ReferralLeaderboardQuery = { __typename?: 'Query', farmers: Array<{ __typename?: 'Farmer', id: any, refereeCount: number, totalReferralRewardPodsReceived: any }> }; + export type FarmerSeasonalSiloQueryVariables = Exact<{ from?: InputMaybe; to?: InputMaybe; @@ -14188,6 +14204,8 @@ export const AllMarketActivityDocument = {"kind":"Document","definitions":[{"kin export const AllPodListingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllPodListings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"1000"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"status"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MarketStatus"}},"defaultValue":{"kind":"EnumValue","value":"ACTIVE"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"maxHarvestableIndex"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skip"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"podListings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"skip"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skip"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"status"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"maxHarvestableIndex_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"maxHarvestableIndex"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"remainingAmount_gt"},"value":{"kind":"StringValue","value":"100000","block":false}}]}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"index"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"asc"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodListing"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodListing"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodListing"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"start"}},{"kind":"Field","name":{"kind":"Name","value":"mode"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxHarvestableIndex"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"originalIndex"}},{"kind":"Field","name":{"kind":"Name","value":"originalPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"originalAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filled"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"remainingAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filledAmount"}},{"kind":"Field","name":{"kind":"Name","value":"fill"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"placeInLine"}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}}]} as unknown as DocumentNode; export const AllPodOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllPodOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"1000"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"status"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MarketStatus"}},"defaultValue":{"kind":"EnumValue","value":"ACTIVE"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skip"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"podOrders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"skip"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skip"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"createdAt"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"status"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodOrder"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodOrder"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodOrder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmount"}},{"kind":"Field","name":{"kind":"Name","value":"podAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}}]} as unknown as DocumentNode; export const FarmerMarketActivityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FarmerMarketActivity"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"1000"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"account"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"listings_createdAt_gt"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orders_createdAt_gt"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"podListings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"farmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"listings_createdAt_gt"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"status_not"},"value":{"kind":"EnumValue","value":"FILLED_PARTIAL"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodListing"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podOrders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"createdAt"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"farmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orders_createdAt_gt"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodOrder"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podFills"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"fromFarmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"toFarmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}}]}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodFill"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodListing"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodListing"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"start"}},{"kind":"Field","name":{"kind":"Name","value":"mode"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxHarvestableIndex"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"originalIndex"}},{"kind":"Field","name":{"kind":"Name","value":"originalPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"originalAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filled"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"remainingAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filledAmount"}},{"kind":"Field","name":{"kind":"Name","value":"fill"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"placeInLine"}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodOrder"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodOrder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmount"}},{"kind":"Field","name":{"kind":"Name","value":"podAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodFill"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodFill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"placeInLine"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"start"}},{"kind":"Field","name":{"kind":"Name","value":"costInBeans"}},{"kind":"Field","name":{"kind":"Name","value":"fromFarmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toFarmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"listing"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"originalAmount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"order"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; +export const FarmerReferralDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FarmerReferral"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"farmer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"totalReferralRewardPodsReceived"}},{"kind":"Field","name":{"kind":"Name","value":"refereeCount"}}]}}]}}]} as unknown as DocumentNode; +export const ReferralLeaderboardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ReferralLeaderboard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skip"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"block"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Block_height"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"farmers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"skip"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skip"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"totalReferralRewardPodsReceived"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"refereeCount_gte"},"value":{"kind":"IntValue","value":"0"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"block"},"value":{"kind":"Variable","name":{"kind":"Name","value":"block"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"refereeCount"}},{"kind":"Field","name":{"kind":"Name","value":"totalReferralRewardPodsReceived"}}]}}]}}]} as unknown as DocumentNode; export const FarmerSeasonalSiloDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FarmerSeasonalSilo"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"from"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"to"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"account"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloHourlySnapshots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"silo"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_gte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"from"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_lte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"to"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"season"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"asc"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"plantedBeans"}},{"kind":"Field","name":{"kind":"Name","value":"stalk"}},{"kind":"Field","name":{"kind":"Name","value":"germinatingStalk"}},{"kind":"Field","name":{"kind":"Name","value":"depositedBDV"}}]}}]}}]} as unknown as DocumentNode; export const FarmerSeasonalSiloAssetTokenDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FarmerSeasonalSiloAssetToken"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"from"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"to"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"siloAsset"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloAssetHourlySnapshots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"siloAsset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"siloAsset"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_gte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"from"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_lte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"to"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"season"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"asc"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"depositedAmount"}},{"kind":"Field","name":{"kind":"Name","value":"depositedBDV"}},{"kind":"Field","name":{"kind":"Name","value":"deltaDepositedBDV"}},{"kind":"Field","name":{"kind":"Name","value":"deltaDepositedAmount"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const BeanstalkSeasonalSiloActiveFarmersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"BeanstalkSeasonalSiloActiveFarmers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"from"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"to"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"silo"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloHourlySnapshots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"season_gte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"from"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_lte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"to"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"silo"},"value":{"kind":"Variable","name":{"kind":"Name","value":"silo"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"stalk_gt"},"value":{"kind":"IntValue","value":"0"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"season"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"activeFarmers"}}]}}]}}]} as unknown as DocumentNode; diff --git a/src/hooks/useFarmerSowEligibility.ts b/src/hooks/useFarmerSowEligibility.ts new file mode 100644 index 00000000..325928cb --- /dev/null +++ b/src/hooks/useFarmerSowEligibility.ts @@ -0,0 +1,48 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { useFarmerField } from "@/state/useFarmerField"; +import { useMemo } from "react"; + +const MINIMUM_SOW_REQUIREMENT = TokenValue.fromHuman("1000", 6); // 1000 Pinto + +export interface FarmerSowEligibility { + /** Total pods owned by farmer */ + totalPods: TokenValue; + /** Whether farmer meets minimum sow requirement */ + meetsRequirement: boolean; + /** Amount still needed to meet requirement */ + amountNeeded: TokenValue; + /** Progress percentage (0-100) */ + progressPercentage: number; +} + +/** + * Hook to check if farmer meets the minimum sow requirement for referrals + */ +export function useFarmerSowEligibility(): FarmerSowEligibility { + const farmerField = useFarmerField(); + + const result = useMemo(() => { + // Calculate total pods from all plots + const totalPods = farmerField.plots.reduce( + (total, plot) => + total.add(plot.unharvestablePods ?? TokenValue.ZERO).add(plot.harvestablePods ?? TokenValue.ZERO), + TokenValue.ZERO, + ); + + const meetsRequirement = totalPods.gte(MINIMUM_SOW_REQUIREMENT); + const amountNeeded = meetsRequirement ? TokenValue.ZERO : MINIMUM_SOW_REQUIREMENT.sub(totalPods); + + const progressPercentage = totalPods.isZero + ? 0 + : Math.min(100, totalPods.div(MINIMUM_SOW_REQUIREMENT).mul(100).toNumber()); + + return { + totalPods, + meetsRequirement, + amountNeeded, + progressPercentage, + }; + }, [farmerField.plots]); + + return result; +} diff --git a/src/pages/Referral.tsx b/src/pages/Referral.tsx new file mode 100644 index 00000000..da62c635 --- /dev/null +++ b/src/pages/Referral.tsx @@ -0,0 +1,72 @@ +import { DelegateReferralModal } from "@/components/DelegateReferralModal"; +import { HowToCard } from "@/components/HowToCard"; +import ReferralLeaderboard from "@/components/ReferralLeaderboard"; +import { ReferralLinkGenerator } from "@/components/ReferralLinkGenerator"; +import { ReferralStatsCard } from "@/components/ReferralStatsCard"; +import { Card } from "@/components/ui/Card"; +import PageContainer from "@/components/ui/PageContainer"; +import { Separator } from "@/components/ui/Separator"; +import { AnimatePresence, motion } from "framer-motion"; +import { useState } from "react"; + +export default function Referral() { + const [isDelegateModalOpen, setIsDelegateModalOpen] = useState(false); + + return ( + +
+
+ {/* Hero Section */} +
+
Referral Program
+
+ Earn rewards by referring new farmers to Pinto. Share your referral link and earn 10% of the Pods your + referrals Sow. +
+
+ + + {/* Main Referral Cards - Two Column Layout */} +
+ {/* Invite via - with overlay pattern like Field page */} +
+ + setIsDelegateModalOpen(true)} /> + + + {isDelegateModalOpen && ( + + +
+ +
+
+
+ )} +
+
+ + {/* How to */} + + + +
+ + {/* Your Referral Stats - Standalone Section */} + + + + + {/* Leaderboard */} + +
+
+
+ ); +} diff --git a/src/queries/beanstalk/referral/FarmerReferral.graphql b/src/queries/beanstalk/referral/FarmerReferral.graphql new file mode 100644 index 00000000..66eea14f --- /dev/null +++ b/src/queries/beanstalk/referral/FarmerReferral.graphql @@ -0,0 +1,7 @@ +query FarmerReferral($id: ID!) { + farmer(id: $id) { + id + totalReferralRewardPodsReceived + refereeCount + } +} \ No newline at end of file diff --git a/src/queries/beanstalk/referral/ReferralLeaderboard.graphql b/src/queries/beanstalk/referral/ReferralLeaderboard.graphql new file mode 100644 index 00000000..bb13969a --- /dev/null +++ b/src/queries/beanstalk/referral/ReferralLeaderboard.graphql @@ -0,0 +1,14 @@ +query ReferralLeaderboard($first: Int!, $skip: Int!, $block: Block_height) { + farmers( + first: $first + skip: $skip + orderBy: totalReferralRewardPodsReceived + orderDirection: desc + where: { refereeCount_gte: 0 } + block: $block + ) { + id + refereeCount + totalReferralRewardPodsReceived + } +} diff --git a/src/state/referral/index.ts b/src/state/referral/index.ts new file mode 100644 index 00000000..ea631632 --- /dev/null +++ b/src/state/referral/index.ts @@ -0,0 +1,8 @@ +export { useReferralLeaderboard } from "./useReferralLeaderboard"; +export type { LeaderboardEntry, UseReferralLeaderboardReturn } from "./useReferralLeaderboard"; + +export { useFarmerReferralData } from "./useFarmerReferralData"; +export type { FarmerReferralStats, UseFarmerReferralDataReturn } from "./useFarmerReferralData"; + +export { useReferralData } from "./useReferralData"; +export type { UseReferralDataReturn } from "./useReferralData"; diff --git a/src/state/referral/useFarmerReferralData.ts b/src/state/referral/useFarmerReferralData.ts new file mode 100644 index 00000000..40e20642 --- /dev/null +++ b/src/state/referral/useFarmerReferralData.ts @@ -0,0 +1,64 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { PODS } from "@/constants/internalTokens"; +import { subgraphs } from "@/constants/subgraph"; +import type { FarmerReferralQuery, FarmerReferralQueryVariables } from "@/generated/gql/pintostalk/graphql"; +import { FarmerReferralDocument } from "@/generated/gql/pintostalk/graphql"; +import { useQuery } from "@tanstack/react-query"; +import request from "graphql-request"; +import { useAccount, useChainId } from "wagmi"; + +export interface FarmerReferralStats { + /** Total referral reward pods earned */ + totalPodsEarned: TokenValue; + /** Number of successful referrals */ + refereeCount: number; + /** Farmer's wallet address */ + address: string; +} + +export interface UseFarmerReferralDataReturn { + data: FarmerReferralStats | null; + isLoading: boolean; + error: Error | null; + refetch: () => void; +} + +/** + * Hook for fetching individual farmer's referral data using codegen generated query + */ +export function useFarmerReferralData(): UseFarmerReferralDataReturn { + const { address } = useAccount(); + const chainId = useChainId(); + + const query = useQuery({ + queryKey: ["farmerReferral", address, chainId], + queryFn: async (): Promise => { + if (!address) throw new Error("No wallet address"); + + const variables: FarmerReferralQueryVariables = { + id: address.toLowerCase(), + }; + + return request(subgraphs[chainId].beanstalk, FarmerReferralDocument, variables); + }, + select: (data: FarmerReferralQuery): FarmerReferralStats | null => { + if (!data.farmer) return null; + + return { + totalPodsEarned: TokenValue.fromBlockchain(data.farmer.totalReferralRewardPodsReceived, PODS.decimals), + refereeCount: data.farmer.refereeCount, + address: data.farmer.id, + }; + }, + enabled: !!address && !!chainId, + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 10 * 60 * 1000, // 10 minutes + }); + + return { + data: query.data || null, + isLoading: query.isLoading, + error: query.error, + refetch: query.refetch, + }; +} diff --git a/src/state/referral/useReferralData.ts b/src/state/referral/useReferralData.ts new file mode 100644 index 00000000..e01cf8da --- /dev/null +++ b/src/state/referral/useReferralData.ts @@ -0,0 +1,59 @@ +import { beanstalkAbi } from "@/generated/contractHooks"; +import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; +import { useAccount, useReadContract } from "wagmi"; + +/** + * Return type for the useReferralData hook + */ +export interface UseReferralDataReturn { + /** Delegate address for pod rewards (where pods will be sent) */ + delegateAddress: `0x${string}` | undefined; + /** True while initial data is loading */ + isLoading: boolean; + /** Error object if the query failed */ + error: Error | null; +} + +/** + * Custom React hook to fetch referral-related data from the contract + * + * This hook fetches the delegate address which determines where referral + * reward pods will be sent. + * + * @returns {UseReferralDataReturn} Referral data and query state + * + * @example + * ```tsx + * function ReferralComponent() { + * const { delegateAddress, isLoading } = useReferralData(); + * + * if (isLoading) return ; + * + * return
Pods will be sent to: {delegateAddress}
; + * } + * ``` + */ +export function useReferralData(): UseReferralDataReturn { + const { address } = useAccount(); + const protocolAddress = useProtocolAddress(); + + const { + data: delegateAddress, + isLoading, + error, + } = useReadContract({ + address: protocolAddress, + abi: beanstalkAbi, + functionName: "getDelegate", + args: address ? [address] : undefined, + query: { + enabled: !!address, + }, + }); + + return { + delegateAddress: delegateAddress as `0x${string}` | undefined, + isLoading, + error: error as Error | null, + }; +} diff --git a/src/state/referral/useReferralLeaderboard.ts b/src/state/referral/useReferralLeaderboard.ts new file mode 100644 index 00000000..0a59d793 --- /dev/null +++ b/src/state/referral/useReferralLeaderboard.ts @@ -0,0 +1,158 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { PODS } from "@/constants/internalTokens"; +import { subgraphs } from "@/constants/subgraph"; +import type { ReferralLeaderboardQuery, ReferralLeaderboardQueryVariables } from "@/generated/gql/pintostalk/graphql"; +import { ReferralLeaderboardDocument } from "@/generated/gql/pintostalk/graphql"; +import { useLatestBlock } from "@/hooks/useLatestBlock"; +import { PaginationSettings, paginateSubgraph } from "@/utils/paginateSubgraph"; +import { stringEq } from "@/utils/string"; +import { useQuery } from "@tanstack/react-query"; +import { useAccount, useChainId } from "wagmi"; + +/** + * Type aliases for codegen generated types + */ +export type FarmersLeaderboardResponse = ReferralLeaderboardQuery; +export type FarmersLeaderboardVariables = ReferralLeaderboardQueryVariables; +export type Farmer = ReferralLeaderboardQuery["farmers"][0]; + +/** + * Pagination settings configuration for farmers leaderboard using the paginateSubgraph utility. + * Implements robust pagination that prevents duplicate or missing entries during data updates. + */ +export const farmersLeaderboardPaginationSettings: PaginationSettings< + Farmer, + FarmersLeaderboardResponse, + "farmers", + FarmersLeaderboardVariables +> = { + /** Primary property name in the GraphQL response containing the array of farmers */ + primaryPropertyName: "farmers", + /** Field used to identify unique farmers */ + idField: "id", + /** + * Function to generate variables for the next page of results + * @param lastFarmer - The last farmer from the current page + * @param prevVars - Variables used for the previous query + * @returns Variables for next page or undefined to terminate pagination + */ + nextVars: (lastFarmer: Farmer, prevVars: FarmersLeaderboardVariables) => { + // If we have fewer than 1000 results, terminate pagination + if (!lastFarmer) { + return undefined; + } + // Continue pagination with next skip value + return { + ...prevVars, + skip: prevVars.skip + 1000, + }; + }, +}; + +/** + * Processed leaderboard entry for display in the UI + */ +export interface LeaderboardEntry { + /** Farmer's wallet address */ + address: string; + /** Total pods earned from referrals as a TokenValue */ + podsEarned: TokenValue; + /** Number of successful referrals made */ + totalSuccessfulReferrals: number; + /** Rank position in the leaderboard (1-based) */ + rank: number; +} + +/** + * Stable select function to transform farmers data to leaderboard entries + * Extracted outside the hook to maintain stable reference and prevent unnecessary re-renders + */ +const selectLeaderboardEntries = (farmers: Farmer[]): LeaderboardEntry[] => { + // Transform farmers to leaderboard entries with proper ranking + return farmers.map((farmer, index) => ({ + address: farmer.id, + podsEarned: TokenValue.fromBlockchain(farmer.totalReferralRewardPodsReceived, PODS.decimals), + totalSuccessfulReferrals: farmer.refereeCount, + rank: index + 1, // Rank based on sorted order from subgraph + })); +}; + +export interface UseReferralLeaderboardReturn { + data: LeaderboardEntry[]; + isLoading: boolean; + error: Error | null; + refetch: () => void; + userRank: number | null; +} + +/** + * Hook for fetching referral leaderboard data using codegen generated query + */ +export function useReferralLeaderboard(): UseReferralLeaderboardReturn { + const chainId = useChainId(); + const { address: userAddress } = useAccount(); + const { data: latestBlock, isLoading: isBlockLoading } = useLatestBlock(); + + // Create stable query key that includes block height when available + const blockHeight = latestBlock?.number ? Number(latestBlock.number) : null; + const queryKey = ["referralLeaderboard", chainId.toString(), blockHeight?.toString() || "latest"]; + + const query = useQuery({ + queryKey, + queryFn: async () => { + const initialVars: FarmersLeaderboardVariables = { + first: 1000, + skip: 0, + }; + + // Include block parameter for pagination session consistency when available + if (blockHeight) { + initialVars.block = { number: blockHeight }; + } + + // Execute pagination query + return paginateSubgraph( + farmersLeaderboardPaginationSettings, + subgraphs[chainId].beanstalk, + ReferralLeaderboardDocument, + initialVars, + ); + }, + select: selectLeaderboardEntries, + enabled: !!chainId && !isBlockLoading, + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 10 * 60 * 1000, // 10 minutes + }); + + // Calculate user's rank in the leaderboard + const userRank = (() => { + // If user is not connected, return null + if (!userAddress) { + return null; + } + + // If data is not loaded yet, return null + if (!query.data) { + return null; + } + + // Find user's address in the leaderboard (case-insensitive comparison) + const userIndex = query.data.findIndex((entry) => stringEq(entry.address, userAddress)); + + // If user is not found in the leaderboard, return null + if (userIndex === -1) { + return null; + } + + // Return 1-based rank (index + 1) + return userIndex + 1; + })(); + + return { + data: query.data || [], + isLoading: query.isLoading || isBlockLoading, + error: query.error, + refetch: query.refetch, + userRank, + }; +} diff --git a/src/utils/referral.ts b/src/utils/referral.ts new file mode 100644 index 00000000..b42d54ba --- /dev/null +++ b/src/utils/referral.ts @@ -0,0 +1,92 @@ +import type { Address } from "viem"; +import { isAddress, zeroAddress } from "viem"; + +/** + * Encode an Ethereum address to base64 for URL shortening + * Treats the address as raw bytes (20 bytes) and encodes to base64 + * + * @param address - Ethereum address (0x prefixed hex string) + * @returns Base64 encoded string (28 characters) + */ +export function encodeReferralAddress(address: Address): string { + // Remove 0x prefix + const hex = address.slice(2); + + // Convert hex string to byte array + const hexPairs = hex.match(/.{1,2}/g) || []; + const bytes = new Uint8Array(hexPairs.map((byte) => Number.parseInt(byte, 16))); + + // Convert bytes to base64 + const base64 = btoa(String.fromCharCode(...bytes)); + + return base64; +} + +/** + * Decode a base64 referral code back to an Ethereum address + * + * @param encoded - Base64 encoded referral code + * @returns Ethereum address or null if invalid + */ +export function decodeReferralAddress(encoded: string): Address | null { + try { + // Decode base64 to bytes + const decoded = atob(encoded); + const bytes = new Uint8Array(decoded.length); + for (let i = 0; i < decoded.length; i++) { + bytes[i] = decoded.charCodeAt(i); + } + + // Convert bytes to hex string + const hex = `0x${Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join("")}`; + + // Validate it's a proper address + if (!isAddress(hex)) return null; + if (hex === zeroAddress) return null; + + return hex as Address; + } catch { + return null; + } +} + +/** + * Check if a referral code is valid (can be decoded to a valid address) + * + * @param code - Base64 encoded referral code + * @returns true if valid, false otherwise + */ +export function isValidReferralCode(code: string): boolean { + return decodeReferralAddress(code) !== null; +} + +/** + * Calculate Pinto sown from referee pods and temperature + * Formula: refereePods / (1 + temperature/100) + * + * @param refereePods - Pods earned by referee (BigInt string) + * @param temperature - Temperature at time of sow (percentage, e.g., 500 = 500%) + * @returns Calculated Pinto sown as BigInt string + */ +export function calculatePintoSown(refereePods: string, temperature: number): string { + const pods = BigInt(refereePods); + // Temperature is in percentage (e.g., 500 means 500%) + // Formula: pods / (1 + temperature/100) = pods * 100 / (100 + temperature) + const temperatureFactor = BigInt(100 + temperature); + const pintoSown = (pods * 100n) / temperatureFactor; + return pintoSown.toString(); +} + +/** + * Calculate total pods created from referrer and referee pods + * + * @param referrerPods - Pods earned by referrer (BigInt string) + * @param refereePods - Pods earned by referee (BigInt string) + * @returns Total pods created as BigInt string + */ +export function calculateTotalPodsCreated(referrerPods: string, refereePods: string): string { + const total = BigInt(referrerPods) + BigInt(refereePods); + return total.toString(); +} diff --git a/src/utils/types.ts b/src/utils/types.ts index 8e65bcec..26670e0b 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -402,3 +402,70 @@ export interface MinimumViableBlock { number: T; timestamp: T; } + +// ---------- REFERRAL LEADERBOARD TYPES ---------- + +/** + * Farmer entity with referral-specific fields + */ +export interface ReferralFarmer { + /** Ethereum address of the farmer */ + id: string; + /** Number of farmers who used this farmer's referral link */ + refereeCount: number; + /** Total pods earned from referral rewards (as string for BigInt compatibility) */ + totalReferralRewardPodsReceived: string; +} + +/** + * Response type for ReferralLeaderboard query + */ +export interface ReferralLeaderboardQuery { + farmers: ReferralFarmer[]; +} + +/** + * Variables for ReferralLeaderboard query + */ +export interface ReferralLeaderboardQueryVariables { + /** Maximum number of farmers to return */ + first?: number; + /** Number of farmers to skip (for pagination) */ + skip?: number; +} + +/** + * Plot entity for referral-sourced plots + */ +export interface ReferralPlot { + /** Unique identifier for the plot */ + id: string; + /** Total pods in the plot (as string for BigInt compatibility) */ + pods: string; + /** Plot index */ + index: string; + /** Farmer who owns this plot */ + farmer: { + /** Ethereum address of the farmer */ + id: string; + }; +} + +/** + * Response type for ReferralPlots query + */ +export interface ReferralPlotsQuery { + plots: ReferralPlot[]; +} + +/** + * Variables for ReferralPlots query + */ +export interface ReferralPlotsQueryVariables { + /** Ethereum address of the farmer to query plots for */ + farmer: string; + /** Maximum number of plots to return */ + first?: number; + /** Number of plots to skip (for pagination) */ + skip?: number; +}