From 2367e441d52d31ebe5b42cfd2e4d49512593c8cb Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:29:48 +0000 Subject: [PATCH 01/22] fix: update bonus pods from 10% to 5% in sow order form Fixes #404 Co-authored-by: fr1jo --- src/components/Tractor/form/SowOrderV0Fields.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Tractor/form/SowOrderV0Fields.tsx b/src/components/Tractor/form/SowOrderV0Fields.tsx index 0d6e7d64..b210b0fb 100644 --- a/src/components/Tractor/form/SowOrderV0Fields.tsx +++ b/src/components/Tractor/form/SowOrderV0Fields.tsx @@ -496,7 +496,7 @@ SowOrderV0Fields.MorningAuction = function MorningAuction() { // TODO: ADD REFERRAL CODE VALIDATOR! -const BONUS_MULTIPLIER = 0.1; +const BONUS_MULTIPLIER = 0.05; SowOrderV0Fields.PodDisplay = function PodDisplay({ onOpenReferralPopover, From 2c18f541332703f97af423b13f484b1f9a5ec58c Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Wed, 4 Feb 2026 04:22:25 +0300 Subject: [PATCH 02/22] feat: implement dashboard for legacy beanstalk holder obligations --- src/ProtectedLayout.tsx | 9 + src/components/BeanstalkStatField.tsx | 64 ++++ src/components/nav/nav/Navbar.tsx | 1 + src/components/nav/nav/Navi.desktop.tsx | 3 + src/components/nav/nav/Navi.mobile.tsx | 3 + src/components/ui/Button.tsx | 3 +- src/constants/meta.ts | 6 + src/pages/Beanstalk.tsx | 53 +++ .../components/BeanstalkFertilizerSection.tsx | 39 +++ .../components/BeanstalkGlobalCard.tsx | 66 ++++ .../components/BeanstalkObligationsCard.tsx | 47 +++ .../components/BeanstalkPodsSection.tsx | 53 +++ .../components/BeanstalkSiloSection.tsx | 39 +++ src/state/useBeanstalkGlobalStats.ts | 153 +++++++++ src/state/useFarmerBeanstalkRepayment.ts | 313 ++++++++++++++++++ 15 files changed, 851 insertions(+), 1 deletion(-) create mode 100644 src/components/BeanstalkStatField.tsx create mode 100644 src/pages/Beanstalk.tsx create mode 100644 src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx create mode 100644 src/pages/beanstalk/components/BeanstalkGlobalCard.tsx create mode 100644 src/pages/beanstalk/components/BeanstalkObligationsCard.tsx create mode 100644 src/pages/beanstalk/components/BeanstalkPodsSection.tsx create mode 100644 src/pages/beanstalk/components/BeanstalkSiloSection.tsx create mode 100644 src/state/useBeanstalkGlobalStats.ts create mode 100644 src/state/useFarmerBeanstalkRepayment.ts diff --git a/src/ProtectedLayout.tsx b/src/ProtectedLayout.tsx index 630ae118..ff817024 100644 --- a/src/ProtectedLayout.tsx +++ b/src/ProtectedLayout.tsx @@ -2,6 +2,7 @@ import { isDev } from "@/utils/utils"; import { Navigate, Route, Routes } from "react-router-dom"; import DevPage from "./components/DevPage"; import PageMetaWrapper from "./components/PageMetaWrapper"; +import Beanstalk from "./pages/Beanstalk"; import Collection from "./pages/Collection"; import Error404 from "./pages/Error404"; import Explorer from "./pages/Explorer"; @@ -69,6 +70,14 @@ export default function ProtectedLayout() { } /> + + + + } + /> void; + disabled?: boolean; +} + +interface BeanstalkStatFieldProps { + title: string; + value: ReactNode; + isLoading?: boolean; + disabled?: boolean; + actions?: BeanstalkStatFieldAction[]; + children?: ReactNode; +} + +/** + * Reusable stat field component with title, value, and optional action buttons + * Used in Beanstalk obligations and global stats cards + */ +const BeanstalkStatField: React.FC = ({ + title, + value, + isLoading = false, + disabled = false, + actions, + children, +}) => { + return ( +
+
+
{title}
+ {actions && actions.length > 0 && ( +
+ {actions.map((action) => ( + + ))} +
+ )} +
+ {children ? ( + children + ) : ( + +
{disabled ? N/A : value}
+
+ )} +
+ ); +}; + +export default BeanstalkStatField; diff --git a/src/components/nav/nav/Navbar.tsx b/src/components/nav/nav/Navbar.tsx index 39d9c9a8..aa4729fb 100644 --- a/src/components/nav/nav/Navbar.tsx +++ b/src/components/nav/nav/Navbar.tsx @@ -332,6 +332,7 @@ export const navLinks = { field: "/field", swap: "/swap", referral: "/referral", + beanstalk: "/beanstalk", 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 da6f6eec..4a6072f8 100644 --- a/src/components/nav/nav/Navi.desktop.tsx +++ b/src/components/nav/nav/Navi.desktop.tsx @@ -110,6 +110,9 @@ const AppNavi = () => { Referral + + Beanstalk + sPinto diff --git a/src/components/nav/nav/Navi.mobile.tsx b/src/components/nav/nav/Navi.mobile.tsx index 7581cce5..c57cad24 100644 --- a/src/components/nav/nav/Navi.mobile.tsx +++ b/src/components/nav/nav/Navi.mobile.tsx @@ -175,6 +175,9 @@ function MobileNavContent({ learnOpen, setLearnOpen, unmount, close }: IMobileNa > Referral + + Beanstalk + = { description: "Share Pinto and earn rewards through referrals.", url: "https://pinto.money/referral", }, + beanstalk: { + title: "Beanstalk Obligations | Pinto", + description: "View your legacy Beanstalk holder obligations including Silo Payback, Pods, and Fertilizer.", + url: "https://pinto.money/beanstalk", + }, }; export default PINTO_META; diff --git a/src/pages/Beanstalk.tsx b/src/pages/Beanstalk.tsx new file mode 100644 index 00000000..d05cc05a --- /dev/null +++ b/src/pages/Beanstalk.tsx @@ -0,0 +1,53 @@ +import ReadMoreAccordion from "@/components/ReadMoreAccordion"; +import { Card } from "@/components/ui/Card"; +import PageContainer from "@/components/ui/PageContainer"; +import { Separator } from "@/components/ui/Separator"; +import BeanstalkGlobalCard from "./beanstalk/components/BeanstalkGlobalCard"; +import BeanstalkObligationsCard from "./beanstalk/components/BeanstalkObligationsCard"; + +const Beanstalk = () => { + return ( + +
+
+ {/* Hero Section */} +
+
Beanstalk Obligations
+
+ Beanstalk Debt issued by Pinto. + + Beanstalk participants at the time of Pinto launch were issued assets based on their holdings. When + Pinto exceeds 1 Billion in supply, 3% of mints go towards these between Beanstalk Silo Tokens, Pods, and + Fertilizer.{" "} + + Learn more + + +
+
+ + + {/* Main Cards - Two Column Layout */} +
+ {/* Left Panel - Obligations Card */} + + + + + {/* Right Panel - Global Stats Card */} + + + +
+
+
+
+ ); +}; + +export default Beanstalk; diff --git a/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx b/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx new file mode 100644 index 00000000..6ab7e299 --- /dev/null +++ b/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx @@ -0,0 +1,39 @@ +import { TokenValue } from "@/classes/TokenValue"; +import BeanstalkStatField from "@/components/BeanstalkStatField"; +import { formatter } from "@/utils/format"; + +interface BeanstalkFertilizerSectionProps { + balance: TokenValue; + isLoading: boolean; + disabled?: boolean; + onRinse?: () => void; + onSend?: () => void; +} + +/** + * Section component displaying fertilizer balance + */ +const BeanstalkFertilizerSection: React.FC = ({ + balance, + isLoading, + disabled = false, + onRinse, + onSend, +}) => { + const hasBalance = !balance.isZero; + + return ( + + ); +}; + +export default BeanstalkFertilizerSection; diff --git a/src/pages/beanstalk/components/BeanstalkGlobalCard.tsx b/src/pages/beanstalk/components/BeanstalkGlobalCard.tsx new file mode 100644 index 00000000..30e791a8 --- /dev/null +++ b/src/pages/beanstalk/components/BeanstalkGlobalCard.tsx @@ -0,0 +1,66 @@ +import BeanstalkStatField from "@/components/BeanstalkStatField"; +import { Button } from "@/components/ui/Button"; +import { useBeanstalkGlobalStats } from "@/state/useBeanstalkGlobalStats"; +import { formatter } from "@/utils/format"; + +/** + * Component displaying global Beanstalk repayment statistics + * Shows total urBDV distributed, total pods in repayment field, + * total unfertilized sprouts, and total Pinto paid out + * Shows N/A values when data cannot be loaded + */ +const BeanstalkGlobalCard: React.FC = () => { + const { + totalUrBdvDistributed, + totalPodsInRepaymentField, + totalUnfertilizedSprouts, + totalPintoPaidOut, + isLoading, + isError, + refetch, + } = useBeanstalkGlobalStats(); + + const formatValue = (value: typeof totalUrBdvDistributed) => { + return formatter.number(value, { minDecimals: 2, maxDecimals: 2 }); + }; + + return ( +
+ {isError && ( +
+ +
+ )} +
+ + + + +
+
+ ); +}; + +export default BeanstalkGlobalCard; diff --git a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx new file mode 100644 index 00000000..2bc0ad65 --- /dev/null +++ b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx @@ -0,0 +1,47 @@ +import { Button } from "@/components/ui/Button"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { useAccount } from "wagmi"; +import BeanstalkFertilizerSection from "./BeanstalkFertilizerSection"; +import BeanstalkPodsSection from "./BeanstalkPodsSection"; +import BeanstalkSiloSection from "./BeanstalkSiloSection"; + +/** + * Container component for displaying user's Beanstalk obligations + * Shows Silo Payback (urBDV), Pods from repayment field, and Fertilizer data + * Shows N/A values when wallet is not connected or data cannot be loaded + */ +const BeanstalkObligationsCard: React.FC = () => { + const account = useAccount(); + const { silo, pods, fertilizer, isLoading, isError, refetch } = useFarmerBeanstalkRepayment(); + + const isConnected = !!account.address; + const showDisabled = !isConnected || isError; + + return ( +
+ {isConnected && isError && ( +
+ +
+ )} +
+ + + +
+
+ ); +}; + +export default BeanstalkObligationsCard; diff --git a/src/pages/beanstalk/components/BeanstalkPodsSection.tsx b/src/pages/beanstalk/components/BeanstalkPodsSection.tsx new file mode 100644 index 00000000..e26a2f5d --- /dev/null +++ b/src/pages/beanstalk/components/BeanstalkPodsSection.tsx @@ -0,0 +1,53 @@ +import { TokenValue } from "@/classes/TokenValue"; +import BeanstalkStatField from "@/components/BeanstalkStatField"; +import PodLineGraph from "@/components/PodLineGraph"; +import TextSkeleton from "@/components/TextSkeleton"; +import { Plot } from "@/utils/types"; + +interface BeanstalkPodsSectionProps { + plots: Plot[]; + totalPods: TokenValue; + isLoading: boolean; + disabled?: boolean; + onHarvest?: () => void; + onSend?: () => void; +} + +/** + * Section component displaying pods from the repayment field (fieldId=1) + * Shows PodLineGraph visualization + */ +const BeanstalkPodsSection: React.FC = ({ + plots, + totalPods, + isLoading, + disabled = false, + onHarvest, + onSend, +}) => { + const hasPlots = plots.length > 0; + const hasPods = !totalPods.isZero; + const showDisabledGraph = disabled || !hasPlots; + + return ( + + {isLoading ? ( + + ) : ( +
+ +
+ )} +
+ ); +}; + +export default BeanstalkPodsSection; diff --git a/src/pages/beanstalk/components/BeanstalkSiloSection.tsx b/src/pages/beanstalk/components/BeanstalkSiloSection.tsx new file mode 100644 index 00000000..c24ca4dd --- /dev/null +++ b/src/pages/beanstalk/components/BeanstalkSiloSection.tsx @@ -0,0 +1,39 @@ +import { TokenValue } from "@/classes/TokenValue"; +import BeanstalkStatField from "@/components/BeanstalkStatField"; +import { formatter } from "@/utils/format"; + +interface BeanstalkSiloSectionProps { + balance: TokenValue; + isLoading: boolean; + disabled?: boolean; + onClaim?: () => void; + onSend?: () => void; +} + +/** + * Section component displaying urBDV token balance for Silo Payback + */ +const BeanstalkSiloSection: React.FC = ({ + balance, + isLoading, + disabled = false, + onClaim, + onSend, +}) => { + const hasBalance = !balance.isZero; + + return ( + + ); +}; + +export default BeanstalkSiloSection; diff --git a/src/state/useBeanstalkGlobalStats.ts b/src/state/useBeanstalkGlobalStats.ts new file mode 100644 index 00000000..315d47db --- /dev/null +++ b/src/state/useBeanstalkGlobalStats.ts @@ -0,0 +1,153 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { PODS } from "@/constants/internalTokens"; +import { defaultQuerySettingsMedium } from "@/constants/query"; +import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; +import { useCallback, useMemo } from "react"; +import { useReadContracts } from "wagmi"; + +/** + * ABI snippets for Silo Payback contract global functions + */ +const siloPaybackGlobalAbi = [ + { + inputs: [], + name: "totalUrBdvDistributed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalPintoPaidOut", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +/** + * ABI snippets for Field contract global functions + */ +const fieldGlobalAbi = [ + { + inputs: [{ internalType: "uint256", name: "fieldId", type: "uint256" }], + name: "totalPods", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +/** + * ABI snippets for Barn Payback contract global functions + */ +const barnPaybackGlobalAbi = [ + { + inputs: [], + name: "totalUnfertilizedSprouts", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +/** + * Interface for the global Beanstalk statistics data + */ +export interface BeanstalkGlobalStatsData { + totalUrBdvDistributed: TokenValue; + totalPodsInRepaymentField: TokenValue; + totalUnfertilizedSprouts: TokenValue; + totalPintoPaidOut: TokenValue; + isLoading: boolean; + isError: boolean; + refetch: () => Promise; +} + +// Token decimals for urBDV (same as BEAN - 6 decimals) +const URBDV_DECIMALS = 6; +// Token decimals for Pinto (6 decimals) +const PINTO_DECIMALS = 6; +// Token decimals for sprouts +const SPROUTS_DECIMALS = 6; + +/** + * Hook for fetching global Beanstalk repayment statistics + * + * This hook fetches protocol-wide statistics: + * - Total urBDV distributed across all holders + * - Total pods in the repayment field (fieldId=1) + * - Total unfertilized sprouts + * - Total Pinto paid out to holders + * + * Uses a 5-minute stale time for more frequent updates of global stats + * + * @returns BeanstalkGlobalStatsData with all global statistics + */ +export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { + const protocolAddress = useProtocolAddress(); + + // Query for all global statistics + const globalQuery = useReadContracts({ + contracts: [ + { + address: protocolAddress, + abi: siloPaybackGlobalAbi, + functionName: "totalUrBdvDistributed", + args: [], + }, + { + address: protocolAddress, + abi: fieldGlobalAbi, + functionName: "totalPods", + args: [1n], // fieldId=1 for repayment field + }, + { + address: protocolAddress, + abi: barnPaybackGlobalAbi, + functionName: "totalUnfertilizedSprouts", + args: [], + }, + { + address: protocolAddress, + abi: siloPaybackGlobalAbi, + functionName: "totalPintoPaidOut", + args: [], + }, + ], + allowFailure: true, + query: { + ...defaultQuerySettingsMedium, // 5 minutes staleTime for global stats + }, + }); + + // Process global data + const globalData = useMemo(() => { + const totalUrBdvDistributed = globalQuery.data?.[0]?.result; + const totalPodsInRepaymentField = globalQuery.data?.[1]?.result; + const totalUnfertilizedSprouts = globalQuery.data?.[2]?.result; + const totalPintoPaidOut = globalQuery.data?.[3]?.result; + + return { + totalUrBdvDistributed: TokenValue.fromBlockchain(totalUrBdvDistributed ?? 0n, URBDV_DECIMALS), + totalPodsInRepaymentField: TokenValue.fromBlockchain(totalPodsInRepaymentField ?? 0n, PODS.decimals), + totalUnfertilizedSprouts: TokenValue.fromBlockchain(totalUnfertilizedSprouts ?? 0n, SPROUTS_DECIMALS), + totalPintoPaidOut: TokenValue.fromBlockchain(totalPintoPaidOut ?? 0n, PINTO_DECIMALS), + }; + }, [globalQuery.data]); + + // Refetch function + const refetch = useCallback(async () => { + await globalQuery.refetch(); + }, [globalQuery.refetch]); + + return useMemo( + () => ({ + ...globalData, + isLoading: globalQuery.isLoading, + isError: globalQuery.isError, + refetch, + }), + [globalData, globalQuery.isLoading, globalQuery.isError, refetch], + ); +} diff --git a/src/state/useFarmerBeanstalkRepayment.ts b/src/state/useFarmerBeanstalkRepayment.ts new file mode 100644 index 00000000..8c92ff52 --- /dev/null +++ b/src/state/useFarmerBeanstalkRepayment.ts @@ -0,0 +1,313 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { ZERO_ADDRESS } from "@/constants/address"; +import { PODS } from "@/constants/internalTokens"; +import { defaultQuerySettings } from "@/constants/query"; +import { beanstalkAbi } from "@/generated/contractHooks"; +import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; +import { Plot } from "@/utils/types"; +import { useCallback, useMemo } from "react"; +import { toHex } from "viem"; +import { useAccount, useReadContracts } from "wagmi"; + +/** + * ABI snippets for Silo Payback contract functions + * These are the functions needed to fetch urBDV token data for legacy Beanstalk holders + */ +const siloPaybackAbi = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOfUrBdv", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "earnedUrBdv", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "totalDistributedToAccount", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "totalReceivedByAccount", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +/** + * ABI snippets for Barn Payback contract functions + * These are the functions needed to fetch fertilizer and sprouts data + */ +const barnPaybackAbi = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOfFertilizer", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOfSprouts", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOfFertilized", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "humidity", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +/** + * Interface for silo payback data + */ +interface SiloPaybackData { + balance: TokenValue; + earned: TokenValue; + totalDistributed: TokenValue; + totalReceived: TokenValue; +} + +/** + * Interface for pods data from repayment field (fieldId=1) + */ +interface PodsData { + plots: Plot[]; + totalPods: TokenValue; +} + +/** + * Interface for fertilizer data + */ +interface FertilizerData { + balance: TokenValue; + sprouts: TokenValue; + fertilized: TokenValue; + humidity: TokenValue; +} + +/** + * Interface for the complete farmer Beanstalk repayment data + */ +export interface FarmerBeanstalkRepaymentData { + silo: SiloPaybackData; + pods: PodsData; + fertilizer: FertilizerData; + isLoading: boolean; + isError: boolean; + refetch: () => Promise; +} + +// Token decimals for urBDV (same as BEAN - 6 decimals) +const URBDV_DECIMALS = 6; +// Token decimals for fertilizer amounts +const FERTILIZER_DECIMALS = 6; +// Humidity is typically represented as a percentage with 2 decimal places +const HUMIDITY_DECIMALS = 2; + +/** + * Hook for fetching farmer-specific Beanstalk repayment data + * + * This hook fetches: + * - Silo payback data (urBDV balance, earned, distributed, received) + * - Pods data from repayment field (fieldId=1) + * - Fertilizer data (balance, sprouts, fertilized, humidity) + * + * @returns FarmerBeanstalkRepaymentData with all farmer obligations data + */ +export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { + const account = useAccount(); + const protocolAddress = useProtocolAddress(); + const farmerAddress = account.address ?? ZERO_ADDRESS; + + // Query for silo payback data + const siloQuery = useReadContracts({ + contracts: [ + { + address: protocolAddress, + abi: siloPaybackAbi, + functionName: "balanceOfUrBdv", + args: [farmerAddress], + }, + { + address: protocolAddress, + abi: siloPaybackAbi, + functionName: "earnedUrBdv", + args: [farmerAddress], + }, + { + address: protocolAddress, + abi: siloPaybackAbi, + functionName: "totalDistributedToAccount", + args: [farmerAddress], + }, + { + address: protocolAddress, + abi: siloPaybackAbi, + functionName: "totalReceivedByAccount", + args: [farmerAddress], + }, + ], + allowFailure: true, + query: { + enabled: !!account.address, + ...defaultQuerySettings, // 20 minutes staleTime + }, + }); + + // Query for pods data from repayment field (fieldId=1) + const podsQuery = useReadContracts({ + contracts: [ + { + address: protocolAddress, + abi: beanstalkAbi, + functionName: "getPlotsFromAccount", + args: [farmerAddress, 1n], // fieldId=1 for repayment field + }, + { + address: protocolAddress, + abi: beanstalkAbi, + functionName: "balanceOfPods", + args: [farmerAddress, 1n], // fieldId=1 for repayment field + }, + ], + allowFailure: true, + query: { + enabled: !!account.address, + ...defaultQuerySettings, // 20 minutes staleTime + }, + }); + + // Query for fertilizer data + const fertilizerQuery = useReadContracts({ + contracts: [ + { + address: protocolAddress, + abi: barnPaybackAbi, + functionName: "balanceOfFertilizer", + args: [farmerAddress], + }, + { + address: protocolAddress, + abi: barnPaybackAbi, + functionName: "balanceOfSprouts", + args: [farmerAddress], + }, + { + address: protocolAddress, + abi: barnPaybackAbi, + functionName: "balanceOfFertilized", + args: [farmerAddress], + }, + { + address: protocolAddress, + abi: barnPaybackAbi, + functionName: "humidity", + args: [], + }, + ], + allowFailure: true, + query: { + enabled: !!account.address, + ...defaultQuerySettings, // 20 minutes staleTime + }, + }); + + // Process silo data + const siloData = useMemo((): SiloPaybackData => { + const balance = siloQuery.data?.[0]?.result; + const earned = siloQuery.data?.[1]?.result; + const totalDistributed = siloQuery.data?.[2]?.result; + const totalReceived = siloQuery.data?.[3]?.result; + + return { + balance: TokenValue.fromBlockchain(balance ?? 0n, URBDV_DECIMALS), + earned: TokenValue.fromBlockchain(earned ?? 0n, URBDV_DECIMALS), + totalDistributed: TokenValue.fromBlockchain(totalDistributed ?? 0n, URBDV_DECIMALS), + totalReceived: TokenValue.fromBlockchain(totalReceived ?? 0n, URBDV_DECIMALS), + }; + }, [siloQuery.data]); + + // Process pods data + const podsData = useMemo((): PodsData => { + const plotsResult = podsQuery.data?.[0]?.result as readonly { index: bigint; pods: bigint }[] | undefined; + const totalPodsResult = podsQuery.data?.[1]?.result; + + const plots: Plot[] = (plotsResult ?? []).map((plotData) => { + const index = TokenValue.fromBigInt(plotData.index, PODS.decimals); + const pods = TokenValue.fromBigInt(plotData.pods, PODS.decimals); + + return { + id: index.toHuman(), + idHex: toHex(`${plotData.index}${plotData.pods}`), + index, + pods, + harvestedPods: TokenValue.ZERO, + harvestablePods: TokenValue.ZERO, + unharvestablePods: pods, + }; + }); + + return { + plots, + totalPods: TokenValue.fromBlockchain(totalPodsResult ?? 0n, PODS.decimals), + }; + }, [podsQuery.data]); + + // Process fertilizer data + const fertilizerData = useMemo((): FertilizerData => { + const balance = fertilizerQuery.data?.[0]?.result; + const sprouts = fertilizerQuery.data?.[1]?.result; + const fertilized = fertilizerQuery.data?.[2]?.result; + const humidity = fertilizerQuery.data?.[3]?.result; + + return { + balance: TokenValue.fromBlockchain(balance ?? 0n, FERTILIZER_DECIMALS), + sprouts: TokenValue.fromBlockchain(sprouts ?? 0n, FERTILIZER_DECIMALS), + fertilized: TokenValue.fromBlockchain(fertilized ?? 0n, FERTILIZER_DECIMALS), + humidity: TokenValue.fromBlockchain(humidity ?? 0n, HUMIDITY_DECIMALS), + }; + }, [fertilizerQuery.data]); + + // Refetch all queries + const refetch = useCallback(async () => { + await Promise.all([siloQuery.refetch(), podsQuery.refetch(), fertilizerQuery.refetch()]); + }, [siloQuery.refetch, podsQuery.refetch, fertilizerQuery.refetch]); + + // Combined loading and error states + const isLoading = siloQuery.isLoading || podsQuery.isLoading || fertilizerQuery.isLoading; + const isError = siloQuery.isError || podsQuery.isError || fertilizerQuery.isError; + + return useMemo( + () => ({ + silo: siloData, + pods: podsData, + fertilizer: fertilizerData, + isLoading, + isError, + refetch, + }), + [siloData, podsData, fertilizerData, isLoading, isError, refetch], + ); +} From b6d1d7578a872d766680ba3aa5641162b8f968f6 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Wed, 4 Feb 2026 05:55:26 +0300 Subject: [PATCH 03/22] chore: remove non-existent contract calls and add subgraph TODOs --- src/state/useBeanstalkGlobalStats.ts | 97 ++++----- src/state/useFarmerBeanstalkRepayment.ts | 256 ++++++++--------------- 2 files changed, 125 insertions(+), 228 deletions(-) diff --git a/src/state/useBeanstalkGlobalStats.ts b/src/state/useBeanstalkGlobalStats.ts index 315d47db..0c825214 100644 --- a/src/state/useBeanstalkGlobalStats.ts +++ b/src/state/useBeanstalkGlobalStats.ts @@ -7,23 +7,24 @@ import { useReadContracts } from "wagmi"; /** * ABI snippets for Silo Payback contract global functions + * NOTE: These functions don't exist in the protocol yet - will be indexed from subgraph later */ -const siloPaybackGlobalAbi = [ - { - inputs: [], - name: "totalUrBdvDistributed", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "totalPintoPaidOut", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, -] as const; +// const siloPaybackGlobalAbi = [ +// { +// inputs: [], +// name: "totalUrBdvDistributed", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [], +// name: "totalPintoPaidOut", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// ] as const; /** * ABI snippets for Field contract global functions @@ -40,16 +41,17 @@ const fieldGlobalAbi = [ /** * ABI snippets for Barn Payback contract global functions + * NOTE: This function doesn't exist in the protocol yet - will be indexed from subgraph later */ -const barnPaybackGlobalAbi = [ - { - inputs: [], - name: "totalUnfertilizedSprouts", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, -] as const; +// const barnPaybackGlobalAbi = [ +// { +// inputs: [], +// name: "totalUnfertilizedSprouts", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// ] as const; /** * Interface for the global Beanstalk statistics data @@ -67,7 +69,7 @@ export interface BeanstalkGlobalStatsData { // Token decimals for urBDV (same as BEAN - 6 decimals) const URBDV_DECIMALS = 6; // Token decimals for Pinto (6 decimals) -const PINTO_DECIMALS = 6; +// const PINTO_DECIMALS = 6; // Token decimals for sprouts const SPROUTS_DECIMALS = 6; @@ -75,10 +77,10 @@ const SPROUTS_DECIMALS = 6; * Hook for fetching global Beanstalk repayment statistics * * This hook fetches protocol-wide statistics: - * - Total urBDV distributed across all holders + * - Total urBDV distributed across all holders (TODO: from subgraph) * - Total pods in the repayment field (fieldId=1) - * - Total unfertilized sprouts - * - Total Pinto paid out to holders + * - Total unfertilized sprouts (TODO: from subgraph) + * - Total Pinto paid out to holders (TODO: from subgraph) * * Uses a 5-minute stale time for more frequent updates of global stats * @@ -87,33 +89,20 @@ const SPROUTS_DECIMALS = 6; export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { const protocolAddress = useProtocolAddress(); - // Query for all global statistics + // Query for available global statistics (only totalPods exists in protocol) const globalQuery = useReadContracts({ contracts: [ - { - address: protocolAddress, - abi: siloPaybackGlobalAbi, - functionName: "totalUrBdvDistributed", - args: [], - }, { address: protocolAddress, abi: fieldGlobalAbi, functionName: "totalPods", args: [1n], // fieldId=1 for repayment field }, - { - address: protocolAddress, - abi: barnPaybackGlobalAbi, - functionName: "totalUnfertilizedSprouts", - args: [], - }, - { - address: protocolAddress, - abi: siloPaybackGlobalAbi, - functionName: "totalPintoPaidOut", - args: [], - }, + // TODO: These functions don't exist in the protocol yet + // Will be indexed from subgraph later: + // - totalUrBdvDistributed + // - totalUnfertilizedSprouts + // - totalPintoPaidOut ], allowFailure: true, query: { @@ -123,16 +112,14 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { // Process global data const globalData = useMemo(() => { - const totalUrBdvDistributed = globalQuery.data?.[0]?.result; - const totalPodsInRepaymentField = globalQuery.data?.[1]?.result; - const totalUnfertilizedSprouts = globalQuery.data?.[2]?.result; - const totalPintoPaidOut = globalQuery.data?.[3]?.result; + const totalPodsInRepaymentField = globalQuery.data?.[0]?.result; return { - totalUrBdvDistributed: TokenValue.fromBlockchain(totalUrBdvDistributed ?? 0n, URBDV_DECIMALS), + // TODO: These will come from subgraph later + totalUrBdvDistributed: TokenValue.fromBlockchain(0n, URBDV_DECIMALS), totalPodsInRepaymentField: TokenValue.fromBlockchain(totalPodsInRepaymentField ?? 0n, PODS.decimals), - totalUnfertilizedSprouts: TokenValue.fromBlockchain(totalUnfertilizedSprouts ?? 0n, SPROUTS_DECIMALS), - totalPintoPaidOut: TokenValue.fromBlockchain(totalPintoPaidOut ?? 0n, PINTO_DECIMALS), + totalUnfertilizedSprouts: TokenValue.fromBlockchain(0n, SPROUTS_DECIMALS), + totalPintoPaidOut: TokenValue.fromBlockchain(0n, URBDV_DECIMALS), }; }, [globalQuery.data]); diff --git a/src/state/useFarmerBeanstalkRepayment.ts b/src/state/useFarmerBeanstalkRepayment.ts index 8c92ff52..28bc670f 100644 --- a/src/state/useFarmerBeanstalkRepayment.ts +++ b/src/state/useFarmerBeanstalkRepayment.ts @@ -11,82 +11,79 @@ import { useAccount, useReadContracts } from "wagmi"; /** * ABI snippets for Silo Payback contract functions - * These are the functions needed to fetch urBDV token data for legacy Beanstalk holders + * NOTE: These functions don't exist in the protocol yet - will be indexed from subgraph later */ -const siloPaybackAbi = [ - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "balanceOfUrBdv", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "earnedUrBdv", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "totalDistributedToAccount", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "totalReceivedByAccount", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, -] as const; +// const siloPaybackAbi = [ +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "balanceOfUrBdv", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "earnedUrBdv", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "totalDistributedToAccount", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "totalReceivedByAccount", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// ] as const; /** * ABI snippets for Barn Payback contract functions - * These are the functions needed to fetch fertilizer and sprouts data + * NOTE: These functions don't exist in the protocol yet - will be indexed from subgraph later */ -const barnPaybackAbi = [ - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "balanceOfFertilizer", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "balanceOfSprouts", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ internalType: "address", name: "account", type: "address" }], - name: "balanceOfFertilized", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [], - name: "humidity", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", - type: "function", - }, -] as const; +// const barnPaybackAbi = [ +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "balanceOfFertilizer", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "balanceOfSprouts", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [{ internalType: "address", name: "account", type: "address" }], +// name: "balanceOfFertilized", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// { +// inputs: [], +// name: "humidity", +// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], +// stateMutability: "view", +// type: "function", +// }, +// ] as const; /** * Interface for silo payback data */ interface SiloPaybackData { balance: TokenValue; - earned: TokenValue; - totalDistributed: TokenValue; - totalReceived: TokenValue; } /** @@ -102,9 +99,6 @@ interface PodsData { */ interface FertilizerData { balance: TokenValue; - sprouts: TokenValue; - fertilized: TokenValue; - humidity: TokenValue; } /** @@ -123,16 +117,14 @@ export interface FarmerBeanstalkRepaymentData { const URBDV_DECIMALS = 6; // Token decimals for fertilizer amounts const FERTILIZER_DECIMALS = 6; -// Humidity is typically represented as a percentage with 2 decimal places -const HUMIDITY_DECIMALS = 2; /** * Hook for fetching farmer-specific Beanstalk repayment data * * This hook fetches: - * - Silo payback data (urBDV balance, earned, distributed, received) - * - Pods data from repayment field (fieldId=1) - * - Fertilizer data (balance, sprouts, fertilized, humidity) + * - Silo payback data (TODO: from subgraph - urBDV balance) + * - Pods data from repayment field (fieldId=1) - from on-chain + * - Fertilizer data (TODO: from subgraph) * * @returns FarmerBeanstalkRepaymentData with all farmer obligations data */ @@ -141,42 +133,8 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { const protocolAddress = useProtocolAddress(); const farmerAddress = account.address ?? ZERO_ADDRESS; - // Query for silo payback data - const siloQuery = useReadContracts({ - contracts: [ - { - address: protocolAddress, - abi: siloPaybackAbi, - functionName: "balanceOfUrBdv", - args: [farmerAddress], - }, - { - address: protocolAddress, - abi: siloPaybackAbi, - functionName: "earnedUrBdv", - args: [farmerAddress], - }, - { - address: protocolAddress, - abi: siloPaybackAbi, - functionName: "totalDistributedToAccount", - args: [farmerAddress], - }, - { - address: protocolAddress, - abi: siloPaybackAbi, - functionName: "totalReceivedByAccount", - args: [farmerAddress], - }, - ], - allowFailure: true, - query: { - enabled: !!account.address, - ...defaultQuerySettings, // 20 minutes staleTime - }, - }); - // Query for pods data from repayment field (fieldId=1) + // These are the only functions that exist in the protocol const podsQuery = useReadContracts({ contracts: [ { @@ -194,62 +152,20 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { ], allowFailure: true, query: { - enabled: !!account.address, - ...defaultQuerySettings, // 20 minutes staleTime - }, - }); - - // Query for fertilizer data - const fertilizerQuery = useReadContracts({ - contracts: [ - { - address: protocolAddress, - abi: barnPaybackAbi, - functionName: "balanceOfFertilizer", - args: [farmerAddress], - }, - { - address: protocolAddress, - abi: barnPaybackAbi, - functionName: "balanceOfSprouts", - args: [farmerAddress], - }, - { - address: protocolAddress, - abi: barnPaybackAbi, - functionName: "balanceOfFertilized", - args: [farmerAddress], - }, - { - address: protocolAddress, - abi: barnPaybackAbi, - functionName: "humidity", - args: [], - }, - ], - allowFailure: true, - query: { - enabled: !!account.address, + // Always fetch - will use ZERO_ADDRESS if wallet not connected ...defaultQuerySettings, // 20 minutes staleTime }, }); - // Process silo data + // TODO: Silo payback data will come from subgraph + // Functions don't exist in protocol: balanceOfUrBdv, earnedUrBdv, totalDistributedToAccount, totalReceivedByAccount const siloData = useMemo((): SiloPaybackData => { - const balance = siloQuery.data?.[0]?.result; - const earned = siloQuery.data?.[1]?.result; - const totalDistributed = siloQuery.data?.[2]?.result; - const totalReceived = siloQuery.data?.[3]?.result; - return { - balance: TokenValue.fromBlockchain(balance ?? 0n, URBDV_DECIMALS), - earned: TokenValue.fromBlockchain(earned ?? 0n, URBDV_DECIMALS), - totalDistributed: TokenValue.fromBlockchain(totalDistributed ?? 0n, URBDV_DECIMALS), - totalReceived: TokenValue.fromBlockchain(totalReceived ?? 0n, URBDV_DECIMALS), + balance: TokenValue.fromBlockchain(0n, URBDV_DECIMALS), }; - }, [siloQuery.data]); + }, []); - // Process pods data + // Process pods data - these functions exist in protocol const podsData = useMemo((): PodsData => { const plotsResult = podsQuery.data?.[0]?.result as readonly { index: bigint; pods: bigint }[] | undefined; const totalPodsResult = podsQuery.data?.[1]?.result; @@ -275,29 +191,23 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { }; }, [podsQuery.data]); - // Process fertilizer data + // TODO: Fertilizer data will come from subgraph + // Functions don't exist in protocol: balanceOfFertilizer, balanceOfSprouts, balanceOfFertilized + // humidity() exists as getCurrentHumidity() but not needed for now const fertilizerData = useMemo((): FertilizerData => { - const balance = fertilizerQuery.data?.[0]?.result; - const sprouts = fertilizerQuery.data?.[1]?.result; - const fertilized = fertilizerQuery.data?.[2]?.result; - const humidity = fertilizerQuery.data?.[3]?.result; - return { - balance: TokenValue.fromBlockchain(balance ?? 0n, FERTILIZER_DECIMALS), - sprouts: TokenValue.fromBlockchain(sprouts ?? 0n, FERTILIZER_DECIMALS), - fertilized: TokenValue.fromBlockchain(fertilized ?? 0n, FERTILIZER_DECIMALS), - humidity: TokenValue.fromBlockchain(humidity ?? 0n, HUMIDITY_DECIMALS), + balance: TokenValue.fromBlockchain(0n, FERTILIZER_DECIMALS), }; - }, [fertilizerQuery.data]); + }, []); - // Refetch all queries + // Refetch pods query (only one that works) const refetch = useCallback(async () => { - await Promise.all([siloQuery.refetch(), podsQuery.refetch(), fertilizerQuery.refetch()]); - }, [siloQuery.refetch, podsQuery.refetch, fertilizerQuery.refetch]); + await podsQuery.refetch(); + }, [podsQuery.refetch]); - // Combined loading and error states - const isLoading = siloQuery.isLoading || podsQuery.isLoading || fertilizerQuery.isLoading; - const isError = siloQuery.isError || podsQuery.isError || fertilizerQuery.isError; + // Loading and error states only from pods query + const isLoading = podsQuery.isLoading; + const isError = podsQuery.isError; return useMemo( () => ({ From 01f94e7d5d7196d8eaaa1452f9fcc21b7f2c80fc Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 12:47:03 +0000 Subject: [PATCH 04/22] refactor: extract repayment field ID to constant Extract hardcoded field ID value (1n) to BEANSTALK_REPAYMENT_FIELD_ID constant for better code maintainability and readability. Co-authored-by: frijo Co-Authored-By: Claude Sonnet 4.5 --- src/state/useBeanstalkGlobalStats.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/state/useBeanstalkGlobalStats.ts b/src/state/useBeanstalkGlobalStats.ts index 0c825214..a29ef48e 100644 --- a/src/state/useBeanstalkGlobalStats.ts +++ b/src/state/useBeanstalkGlobalStats.ts @@ -72,6 +72,8 @@ const URBDV_DECIMALS = 6; // const PINTO_DECIMALS = 6; // Token decimals for sprouts const SPROUTS_DECIMALS = 6; +// Field ID for the Beanstalk repayment field +const BEANSTALK_REPAYMENT_FIELD_ID = 1n; /** * Hook for fetching global Beanstalk repayment statistics @@ -96,7 +98,7 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { address: protocolAddress, abi: fieldGlobalAbi, functionName: "totalPods", - args: [1n], // fieldId=1 for repayment field + args: [BEANSTALK_REPAYMENT_FIELD_ID], }, // TODO: These functions don't exist in the protocol yet // Will be indexed from subgraph later: From 01c3a9df99f23574573297f969f5a28c37a008ac Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 13:19:21 +0000 Subject: [PATCH 05/22] refactor: replace anchor tag with Link component in Beanstalk page Co-authored-by: frijo Co-Authored-By: Claude Sonnet 4.5 --- src/pages/Beanstalk.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/Beanstalk.tsx b/src/pages/Beanstalk.tsx index d05cc05a..7fe23357 100644 --- a/src/pages/Beanstalk.tsx +++ b/src/pages/Beanstalk.tsx @@ -2,6 +2,7 @@ import ReadMoreAccordion from "@/components/ReadMoreAccordion"; import { Card } from "@/components/ui/Card"; import PageContainer from "@/components/ui/PageContainer"; import { Separator } from "@/components/ui/Separator"; +import { Link } from "react-router-dom"; import BeanstalkGlobalCard from "./beanstalk/components/BeanstalkGlobalCard"; import BeanstalkObligationsCard from "./beanstalk/components/BeanstalkObligationsCard"; @@ -19,14 +20,14 @@ const Beanstalk = () => { Beanstalk participants at the time of Pinto launch were issued assets based on their holdings. When Pinto exceeds 1 Billion in supply, 3% of mints go towards these between Beanstalk Silo Tokens, Pods, and Fertilizer.{" "} - Learn more - + From eaf5465160ed01c346888fbdab4295bf5557a2cf Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 6 Feb 2026 15:41:37 +0000 Subject: [PATCH 06/22] refactor: extract urBDV and Sprouts to internal token definitions - Add URBDV and SPROUTS token definitions to internalTokens.ts - Remove hardcoded URBDV_DECIMALS and SPROUTS_DECIMALS constants - Update useBeanstalkGlobalStats to use token instances for decimals Co-authored-by: frijo --- src/constants/internalTokens.ts | 16 ++++++++++++++++ src/state/useBeanstalkGlobalStats.ts | 14 ++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/constants/internalTokens.ts b/src/constants/internalTokens.ts index 954e7e41..b9a9ceb1 100644 --- a/src/constants/internalTokens.ts +++ b/src/constants/internalTokens.ts @@ -27,3 +27,19 @@ export const PODS: InternalToken = { displayDecimals: 2, logoURI: podLogo, }; + +export const URBDV: InternalToken = { + name: "Unripe BDV", + symbol: "urBDV", + decimals: 6, + displayDecimals: 2, + logoURI: "", // TODO: Add logo if needed +}; + +export const SPROUTS: InternalToken = { + name: "Sprouts", + symbol: "SPROUTS", + decimals: 6, + displayDecimals: 2, + logoURI: "", // TODO: Add logo if needed +}; diff --git a/src/state/useBeanstalkGlobalStats.ts b/src/state/useBeanstalkGlobalStats.ts index a29ef48e..4a3518fb 100644 --- a/src/state/useBeanstalkGlobalStats.ts +++ b/src/state/useBeanstalkGlobalStats.ts @@ -1,5 +1,5 @@ import { TokenValue } from "@/classes/TokenValue"; -import { PODS } from "@/constants/internalTokens"; +import { PODS, SPROUTS, URBDV } from "@/constants/internalTokens"; import { defaultQuerySettingsMedium } from "@/constants/query"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import { useCallback, useMemo } from "react"; @@ -66,12 +66,6 @@ export interface BeanstalkGlobalStatsData { refetch: () => Promise; } -// Token decimals for urBDV (same as BEAN - 6 decimals) -const URBDV_DECIMALS = 6; -// Token decimals for Pinto (6 decimals) -// const PINTO_DECIMALS = 6; -// Token decimals for sprouts -const SPROUTS_DECIMALS = 6; // Field ID for the Beanstalk repayment field const BEANSTALK_REPAYMENT_FIELD_ID = 1n; @@ -118,10 +112,10 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { return { // TODO: These will come from subgraph later - totalUrBdvDistributed: TokenValue.fromBlockchain(0n, URBDV_DECIMALS), + totalUrBdvDistributed: TokenValue.fromBlockchain(0n, URBDV.decimals), totalPodsInRepaymentField: TokenValue.fromBlockchain(totalPodsInRepaymentField ?? 0n, PODS.decimals), - totalUnfertilizedSprouts: TokenValue.fromBlockchain(0n, SPROUTS_DECIMALS), - totalPintoPaidOut: TokenValue.fromBlockchain(0n, URBDV_DECIMALS), + totalUnfertilizedSprouts: TokenValue.fromBlockchain(0n, SPROUTS.decimals), + totalPintoPaidOut: TokenValue.fromBlockchain(0n, URBDV.decimals), }; }, [globalQuery.data]); From 4877ee8b5bb0a232a88b2465cd684f60651ad1f3 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Wed, 11 Feb 2026 03:52:04 +0300 Subject: [PATCH 07/22] feat(transfer): add consolidated pod range selection and summary view --- src/pages/transfer/actions/TransferPods.tsx | 12 +- src/pages/transfer/actions/pods/FinalStep.tsx | 52 ++-- src/pages/transfer/actions/pods/StepOne.tsx | 223 +++++++++++++----- src/pages/transfer/actions/pods/StepTwo.tsx | 125 +--------- src/utils/podTransferUtils.ts | 98 ++++++++ 5 files changed, 292 insertions(+), 218 deletions(-) create mode 100644 src/utils/podTransferUtils.ts diff --git a/src/pages/transfer/actions/TransferPods.tsx b/src/pages/transfer/actions/TransferPods.tsx index 54198555..3537ccd7 100644 --- a/src/pages/transfer/actions/TransferPods.tsx +++ b/src/pages/transfer/actions/TransferPods.tsx @@ -39,7 +39,7 @@ export default function TransferPods() { case 1: return "Select Plots"; case 2: - return "Specify amount and address"; + return "Enter address"; default: return "Confirm send"; } @@ -50,13 +50,7 @@ export default function TransferPods() { case 1: return transferData.length > 0; case 2: - if (!!destination && transferNotice) { - if (transferData.length === 1) { - return transferData[0].end.gt(transferData[0].start); - } - return true; - } - return false; + return !!destination && transferNotice; default: return true; } @@ -131,8 +125,6 @@ export default function TransferPods() { ) : step === 2 ? ( { + if (transferData.length === 0) return null; + return computeSummaryRange(transferData, harvestableIndex); + }, [transferData, harvestableIndex]); + + if (!destination || !summary) { return null; } + const { totalPods, placeInLineStart, placeInLineEnd } = summary; + const isSinglePlot = transferData.length === 1; + return (
- {transferData.map((transfer) => { - const placeInLine = transfer.id.sub(harvestableIndex); - const podAmount = transfer.end.sub(transfer.start); - - return ( -
-
- {formatter.number(podAmount)} - Plot - Pods -
-
- @ - {formatter.number(placeInLine.add(transfer.start))} in Line -
-
- ); - })} +
+
+ {formatter.number(totalPods)} + Plot + Pods +
+
+ {isSinglePlot ? ( + <> + @ + {formatter.number(placeInLineStart)} in Line + + ) : ( + + between {formatter.number(placeInLineStart)} - {formatter.number(placeInLineEnd)} in Line + + )} +
+
diff --git a/src/pages/transfer/actions/pods/StepOne.tsx b/src/pages/transfer/actions/pods/StepOne.tsx index 5e08ea3c..9568a1ce 100644 --- a/src/pages/transfer/actions/pods/StepOne.tsx +++ b/src/pages/transfer/actions/pods/StepOne.tsx @@ -1,11 +1,11 @@ -import { TokenValue } from "@/classes/TokenValue"; -import PlotsTable from "@/components/PlotsTable"; -import { Button } from "@/components/ui/Button"; -import { Label } from "@/components/ui/Label"; -import { ToggleGroup } from "@/components/ui/ToggleGroup"; +import PodLineGraph from "@/components/PodLineGraph"; +import { MultiSlider } from "@/components/ui/Slider"; import { useFarmerField } from "@/state/useFarmerField"; +import { useHarvestableIndex } from "@/state/useFieldData"; +import { formatter } from "@/utils/format"; +import { computeTransferData, offsetToAbsoluteIndex } from "@/utils/podTransferUtils"; import { Plot } from "@/utils/types"; -import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; +import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { PodTransferData } from "../TransferPods"; interface StepOneProps { @@ -13,81 +13,176 @@ interface StepOneProps { setTransferData: Dispatch>; } +function sortPlotsByIndex(plots: Plot[]): Plot[] { + return [...plots].sort((a, b) => a.index.sub(b.index).toNumber()); +} + export default function StepOne({ transferData, setTransferData }: StepOneProps) { - const [selected, setSelected] = useState(); const { plots } = useFarmerField(); + const harvestableIndex = useHarvestableIndex(); + + const [selectedPlots, setSelectedPlots] = useState([]); + const [podRange, setPodRange] = useState<[number, number]>([0, 0]); + const mountedRef = useRef(false); + + // Restore selection from existing transferData on mount useEffect(() => { - const _newPlots: string[] = []; - for (const data of transferData) { - const _plot = plots.find((plot) => plot.index.eq(data.id)); - if (_plot) { - _newPlots.push(_plot.index.toHuman()); - } + if (mountedRef.current) return; + mountedRef.current = true; + if (transferData.length === 0) return; + const restoredPlots = transferData + .map((data) => plots.find((p) => p.index.eq(data.id))) + .filter((p): p is Plot => p !== undefined); + if (restoredPlots.length > 0) { + const sorted = sortPlotsByIndex(restoredPlots); + setSelectedPlots(sorted); + const total = sorted.reduce((sum, p) => sum + p.pods.toNumber(), 0); + setPodRange([0, total]); } - setSelected(_newPlots); + // Only run on mount + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // Total pods across selected plots + const totalPods = useMemo(() => { + return selectedPlots.reduce((sum, p) => sum + p.pods.toNumber(), 0); + }, [selectedPlots]); + + // Derived amount from slider range โ€” no separate state needed + const amount = podRange[1] - podRange[0]; + + // Memoize selectedPlotIndices to avoid new array ref each render + const selectedPlotIndices = useMemo(() => selectedPlots.map((p) => p.index.toHuman()), [selectedPlots]); + + // Position info โ€” plots are already sorted, use first/last directly + const positionInfo = useMemo(() => { + if (selectedPlots.length === 0) return null; + const first = selectedPlots[0]; + const last = selectedPlots[selectedPlots.length - 1]; + return { + start: first.index.sub(harvestableIndex), + end: last.index.add(last.pods).sub(harvestableIndex), + }; + }, [selectedPlots, harvestableIndex]); + + // Compute selectedPodRange for PodLineGraph (absolute indices) + const selectedPodRange = useMemo(() => { + if (selectedPlots.length === 0) return undefined; + return { + start: offsetToAbsoluteIndex(podRange[0], selectedPlots), + end: offsetToAbsoluteIndex(podRange[1], selectedPlots), + }; + }, [selectedPlots, podRange]); + + // Handle plot selection changes: sort, reset slider, update transferData const handlePlotSelection = useCallback( - (value: string[]) => { - // Update selected plots - setSelected(value); - - // Get selected plots data - const selectedPlots = value - .map((plotIndex) => { - const plot = plots.find((p) => p.index.toHuman() === plotIndex); - return plot; - }) - .filter((plot): plot is Plot => plot !== undefined && !plot.fullyHarvested); - - // If no valid plots selected, clear transfer data - if (selectedPlots.length === 0) { + (newPlots: Plot[]) => { + const sorted = sortPlotsByIndex(newPlots); + setSelectedPlots(sorted); + + if (sorted.length > 0) { + const newTotal = sorted.reduce((sum, p) => sum + p.pods.toNumber(), 0); + setPodRange([0, newTotal]); + setTransferData(computeTransferData(sorted, [0, newTotal])); + } else { + setPodRange([0, 0]); setTransferData([]); + } + }, + [setTransferData], + ); + + // Toggle logic: if all in group selected โ†’ deselect, else add + const handlePlotGroupSelect = useCallback( + (plotIndices: string[]) => { + const groupSet = new Set(plotIndices); + const plotsInGroup = plots.filter((p) => groupSet.has(p.index.toHuman())); + if (plotsInGroup.length === 0) return; + + const selectedSet = new Set(selectedPlots.map((p) => p.index.toHuman())); + const allSelected = plotIndices.every((idx) => selectedSet.has(idx)); + + if (allSelected) { + handlePlotSelection(selectedPlots.filter((p) => !groupSet.has(p.index.toHuman()))); return; } - // Create plot transfer data - const transferData = selectedPlots.map((plot) => { - return { - id: plot.index, - start: TokenValue.ZERO, - end: plot.pods, - }; - }); - - // Update transfer data - setTransferData(transferData); + const newPlots = [...selectedPlots]; + for (const plotToAdd of plotsInGroup) { + if (!selectedSet.has(plotToAdd.index.toHuman())) { + newPlots.push(plotToAdd); + } + } + handlePlotSelection(newPlots); }, - [plots, setTransferData], + [plots, selectedPlots, handlePlotSelection], ); - const selectAllPlots = useCallback(() => { - const plotIndexes = plots.map((plot) => plot.index.toHuman()); - handlePlotSelection(plotIndexes); - }, [plots, handlePlotSelection]); + // Slider change handler + const handlePodRangeChange = useCallback( + (value: number[]) => { + const newRange: [number, number] = [value[0], value[1]]; + setPodRange(newRange); + setTransferData(computeTransferData(selectedPlots, newRange)); + }, + [selectedPlots, setTransferData], + ); return ( - <> -
- -
-
- - - - +
+ {/* Pod Line Graph Visualization */} +
+ + + {/* Position in Line Display */} + {positionInfo && ( +
+

+ {positionInfo.start.toHuman("short")} - {positionInfo.end.toHuman("short")} +

+
+ )}
- + + {/* Total Pods Summary */} + {totalPods > 0 && ( +
+

Total Pods to send:

+

{formatter.noDec(amount)} Pods

+
+ )} + + {/* MultiSlider for pod range selection */} + {selectedPlots.length > 0 && ( +
+
+

Select Pods

+
+

{formatter.noDec(podRange[0])}

+
+ {totalPods > 0 && ( + + )} +
+

{formatter.noDec(podRange[1])}

+
+
+
+ )} +
); } diff --git a/src/pages/transfer/actions/pods/StepTwo.tsx b/src/pages/transfer/actions/pods/StepTwo.tsx index 1f8c07d7..86237f98 100644 --- a/src/pages/transfer/actions/pods/StepTwo.tsx +++ b/src/pages/transfer/actions/pods/StepTwo.tsx @@ -1,135 +1,19 @@ -import { TokenValue } from "@/classes/TokenValue"; import AddressInputField from "@/components/AddressInputField"; -import { ComboInputField } from "@/components/ComboInputField"; import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; -import PodRangeSelector from "@/components/PodRangeSelector"; import { Label } from "@/components/ui/Label"; -import { PODS } from "@/constants/internalTokens"; -import { useFarmerField } from "@/state/useFarmerField"; -import { Plot } from "@/utils/types"; import { AnimatePresence, motion } from "framer-motion"; -import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; -import { PodTransferData } from "../TransferPods"; +import { Dispatch, SetStateAction } from "react"; interface StepTwoProps { - transferData: PodTransferData[]; - setTransferData: Dispatch>; destination: string | undefined; setDestination: Dispatch>; transferNotice: boolean; setTransferNotice: Dispatch>; } -export default function StepTwo({ - transferData, - setTransferData, - destination, - setDestination, - transferNotice, - setTransferNotice, -}: StepTwoProps) { - const { plots } = useFarmerField(); - const [selectedPlots, setSelectedPlots] = useState([]); - const [amount, setAmount] = useState("0"); - const [range, setRange] = useState<[TokenValue, TokenValue]>([TokenValue.ZERO, TokenValue.ZERO]); - - useEffect(() => { - const _newPlots: Plot[] = []; - for (const data of transferData) { - const _plot = plots.find((plot) => plot.index.eq(data.id)); - if (_plot) { - _newPlots.push(_plot); - } - } - setSelectedPlots(_newPlots); - }, []); - - useEffect(() => { - if (selectedPlots.length === 1) { - const plot = selectedPlots[0]; - setRange([plot.index, plot.index.add(plot.pods)]); - } - }, [selectedPlots]); - - const handleRangeChange = useCallback( - (newRange: TokenValue[]) => { - if (selectedPlots.length === 0 || selectedPlots.length > 1) return; - const plot = selectedPlots[0]; - const newStart = newRange[0]; - const newEnd = newRange[1]; - - const relativeStart = newStart.sub(plot.index); - const relativeEnd = newEnd.sub(plot.index); - - const newData = [ - { - id: plot.index, - start: relativeStart, - end: relativeEnd, - }, - ]; - - const newAmount = newEnd.sub(newStart).toHuman(); - - const batchUpdate = () => { - setRange([newStart, newEnd]); - setTransferData(newData); - setAmount(newAmount); - }; - batchUpdate(); - }, - [selectedPlots, setTransferData], - ); - - const handleAmountChange = useCallback( - (value: string) => { - const newAmount = value; - - if (selectedPlots.length === 0) return; - - const plot = selectedPlots[0]; - const amountValue = TokenValue.fromHuman(newAmount || "0", PODS.decimals); - const newEnd = plot.index.add(amountValue); - - const newStart = plot.index; - const relativeStart = newStart.sub(plot.index); - const relativeEnd = newEnd.sub(plot.index); - - const newData = [ - { - id: plot.index, - start: relativeStart, - end: relativeEnd, - }, - ]; - - const batchUpdate = () => { - setAmount(newAmount); - if (selectedPlots.length === 1) { - setRange([newStart, newEnd]); - setTransferData(newData); - } - }; - batchUpdate(); - }, - [selectedPlots, setTransferData], - ); - +export default function StepTwo({ destination, setDestination, transferNotice, setTransferNotice }: StepTwoProps) { return (
-
- - 1} - altText={selectedPlots.length > 1 ? "Balance:" : "Plot Balance:"} - /> -
@@ -148,11 +32,8 @@ export default function StepTwo({ /> )} - {" "} +
- {selectedPlots.length === 1 && ( - - )}
); } diff --git a/src/utils/podTransferUtils.ts b/src/utils/podTransferUtils.ts new file mode 100644 index 00000000..ebbff175 --- /dev/null +++ b/src/utils/podTransferUtils.ts @@ -0,0 +1,98 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { PODS } from "@/constants/internalTokens"; +import { PodTransferData } from "@/pages/transfer/actions/TransferPods"; +import { Plot } from "@/utils/types"; + +/** + * Converts a cumulative offset range [rangeStart, rangeEnd] across sorted plots + * into per-plot PodTransferData records with relative start/end values. + * + * Adapted from CreateListing's `listingData` useMemo logic. + * + * @param selectedPlots - Plots sorted by index + * @param podRange - [rangeStart, rangeEnd] cumulative offset (0 to totalPods) + * @returns PodTransferData[] with one entry per intersecting plot + */ +export function computeTransferData(selectedPlots: Plot[], podRange: [number, number]): PodTransferData[] { + const result: PodTransferData[] = []; + let cumulativeStart = 0; + + for (const plot of selectedPlots) { + const plotPods = plot.pods.toNumber(); + const cumulativeEnd = cumulativeStart + plotPods; + + // Check if this plot intersects with the selected range + if (podRange[1] > cumulativeStart && podRange[0] < cumulativeEnd) { + const startInPlot = Math.max(0, podRange[0] - cumulativeStart); + const endInPlot = Math.min(plotPods, podRange[1] - cumulativeStart); + + if (endInPlot > startInPlot) { + result.push({ + id: plot.index, + start: TokenValue.fromHuman(startInPlot, PODS.decimals), + end: TokenValue.fromHuman(endInPlot, PODS.decimals), + }); + } + } + + cumulativeStart = cumulativeEnd; + } + + return result; +} + +/** + * Converts a cumulative offset (relative to sorted plots) into an absolute + * TokenValue index on the pod line. + * + * Adapted from CreateListing's `selectedPodRange` useMemo logic. + * + * @param offset - Cumulative offset (0 to totalPods) + * @param sortedPlots - Plots sorted by index + * @returns Absolute TokenValue index on the pod line + */ +export function offsetToAbsoluteIndex(offset: number, sortedPlots: Plot[]): TokenValue { + let remainingOffset = offset; + + for (const plot of sortedPlots) { + const plotPods = plot.pods.toNumber(); + if (remainingOffset <= plotPods) { + return plot.index.add(TokenValue.fromHuman(remainingOffset, PODS.decimals)); + } + remainingOffset -= plotPods; + } + + // Fallback: offset exceeds total pods, clamp to end of last plot + const lastPlot = sortedPlots[sortedPlots.length - 1]; + return lastPlot.index.add(lastPlot.pods); +} + +/** + * Computes a consolidated summary range from transfer data records. + * + * - totalPods: sum of (end - start) across all records + * - placeInLineStart: first record's (id + start) - harvestableIndex + * - placeInLineEnd: last record's (id + end) - harvestableIndex + * + * @param transferData - Array of PodTransferData (must have at least one entry) + * @param harvestableIndex - Current harvestable index on the pod line + * @returns { totalPods, placeInLineStart, placeInLineEnd } + */ +export function computeSummaryRange( + transferData: PodTransferData[], + harvestableIndex: TokenValue, +): { totalPods: TokenValue; placeInLineStart: TokenValue; placeInLineEnd: TokenValue } { + const totalPods = transferData.reduce((sum, record) => sum.add(record.end.sub(record.start)), TokenValue.ZERO); + + const first = transferData[0]; + const last = transferData[transferData.length - 1]; + + const rangeStart = first.id.add(first.start); + const rangeEnd = last.id.add(last.end); + + return { + totalPods, + placeInLineStart: rangeStart.sub(harvestableIndex), + placeInLineEnd: rangeEnd.sub(harvestableIndex), + }; +} From 9368d259a329f94104a50fee92bd2c969fe5725b Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Wed, 11 Feb 2026 04:24:18 +0300 Subject: [PATCH 08/22] feat(market): support marketplace filtering and update plot metadata --- src/generated/gql/exchange/graphql.ts | 94 +++---------------- src/generated/gql/pintostalk/gql.ts | 6 +- src/generated/gql/pintostalk/graphql.ts | 16 +++- src/pages/Market.tsx | 11 ++- .../podmarket/AllMarketActivity.graphql | 13 ++- src/state/market/useAllMarket.ts | 11 ++- 6 files changed, 59 insertions(+), 92 deletions(-) diff --git a/src/generated/gql/exchange/graphql.ts b/src/generated/gql/exchange/graphql.ts index 96429785..bbcf894a 100644 --- a/src/generated/gql/exchange/graphql.ts +++ b/src/generated/gql/exchange/graphql.ts @@ -29,44 +29,6 @@ export type Scalars = { Timestamp: { input: any; output: any; } }; -export type Account = { - __typename?: 'Account'; - id: Scalars['Bytes']['output']; - trades: Array; -}; - - -export type AccountTradesArgs = { - first?: InputMaybe; - orderBy?: InputMaybe; - orderDirection?: InputMaybe; - skip?: InputMaybe; - where?: InputMaybe; -}; - -export type AccountFilter = { - /** Filter for the block changed event. */ - _change_block?: InputMaybe; - and?: InputMaybe>>; - id?: InputMaybe; - id_contains?: InputMaybe; - id_gt?: InputMaybe; - id_gte?: InputMaybe; - id_in?: InputMaybe>; - id_lt?: InputMaybe; - id_lte?: InputMaybe; - id_not?: InputMaybe; - id_not_contains?: InputMaybe; - id_not_in?: InputMaybe>; - or?: InputMaybe>>; - trades_?: InputMaybe; -}; - -export enum AccountOrderBy { - id = 'id', - trades = 'trades' -} - export enum AggregationInterval { day = 'day', hour = 'hour' @@ -1284,6 +1246,7 @@ export type ConvertCandidateFilter = { export enum ConvertCandidateOrderBy { addLiquidityTrade = 'addLiquidityTrade', + addLiquidityTrade__account = 'addLiquidityTrade__account', addLiquidityTrade__blockNumber = 'addLiquidityTrade__blockNumber', addLiquidityTrade__hash = 'addLiquidityTrade__hash', addLiquidityTrade__id = 'addLiquidityTrade__id', @@ -1298,6 +1261,7 @@ export enum ConvertCandidateOrderBy { addLiquidityTrade__transferVolumeUSD = 'addLiquidityTrade__transferVolumeUSD', id = 'id', removeLiquidityTrade = 'removeLiquidityTrade', + removeLiquidityTrade__account = 'removeLiquidityTrade__account', removeLiquidityTrade__blockNumber = 'removeLiquidityTrade__blockNumber', removeLiquidityTrade__hash = 'removeLiquidityTrade__hash', removeLiquidityTrade__id = 'removeLiquidityTrade__id', @@ -1402,8 +1366,6 @@ export type Query = { __typename?: 'Query'; /** Access to subgraph metadata */ _meta?: Maybe; - account?: Maybe; - accounts: Array; aquifer?: Maybe; aquifers: Array; beanstalk?: Maybe; @@ -1444,24 +1406,6 @@ export type QueryMetaArgs = { }; -export type QueryAccountArgs = { - block?: InputMaybe; - id: Scalars['ID']['input']; - subgraphError?: SubgraphErrorPolicy; -}; - - -export type QueryAccountsArgs = { - block?: InputMaybe; - first?: InputMaybe; - orderBy?: InputMaybe; - orderDirection?: InputMaybe; - skip?: InputMaybe; - subgraphError?: SubgraphErrorPolicy; - where?: InputMaybe; -}; - - export type QueryAquiferArgs = { block?: InputMaybe; id: Scalars['ID']['input']; @@ -1953,7 +1897,7 @@ export enum TokenOrderBy { export type Trade = { __typename?: 'Trade'; /** Account that sent this transaction */ - account: Account; + account: Scalars['Bytes']['output']; /** Well.reserves after this event */ afterReserves: Array; /** Well.tokenRates before this event */ @@ -2015,27 +1959,16 @@ export enum TradeType { export type TradeFilter = { /** Filter for the block changed event. */ _change_block?: InputMaybe; - account?: InputMaybe; - account_?: InputMaybe; - account_contains?: InputMaybe; - account_contains_nocase?: InputMaybe; - account_ends_with?: InputMaybe; - account_ends_with_nocase?: InputMaybe; - account_gt?: InputMaybe; - account_gte?: InputMaybe; - account_in?: InputMaybe>; - account_lt?: InputMaybe; - account_lte?: InputMaybe; - account_not?: InputMaybe; - account_not_contains?: InputMaybe; - account_not_contains_nocase?: InputMaybe; - account_not_ends_with?: InputMaybe; - account_not_ends_with_nocase?: InputMaybe; - account_not_in?: InputMaybe>; - account_not_starts_with?: InputMaybe; - account_not_starts_with_nocase?: InputMaybe; - account_starts_with?: InputMaybe; - account_starts_with_nocase?: InputMaybe; + account?: InputMaybe; + account_contains?: InputMaybe; + account_gt?: InputMaybe; + account_gte?: InputMaybe; + account_in?: InputMaybe>; + account_lt?: InputMaybe; + account_lte?: InputMaybe; + account_not?: InputMaybe; + account_not_contains?: InputMaybe; + account_not_in?: InputMaybe>; afterReserves?: InputMaybe>; afterReserves_contains?: InputMaybe>; afterReserves_contains_nocase?: InputMaybe>; @@ -2255,7 +2188,6 @@ export type TradeFilter = { export enum TradeOrderBy { account = 'account', - account__id = 'account__id', afterReserves = 'afterReserves', afterTokenRates = 'afterTokenRates', beforeReserves = 'beforeReserves', diff --git a/src/generated/gql/pintostalk/gql.ts b/src/generated/gql/pintostalk/gql.ts index 508c651f..ec851937 100644 --- a/src/generated/gql/pintostalk/gql.ts +++ b/src/generated/gql/pintostalk/gql.ts @@ -22,7 +22,7 @@ type Documents = { "query BeanstalkSeasonsTable($from: Int, $to: Int) {\n seasons(\n first: 1000\n orderBy: season\n orderDirection: desc\n where: {season_gte: $from, season_lte: $to}\n ) {\n id\n sunriseBlock\n rewardBeans\n price\n deltaBeans\n raining\n season\n }\n fieldHourlySnapshots(\n first: 1000\n orderBy: season\n orderDirection: desc\n where: {field: \"0xd1a0d188e861ed9d15773a2f3574a2e94134ba8f\", season_gte: $from, season_lte: $to}\n ) {\n id\n caseId\n issuedSoil\n deltaSownBeans\n sownBeans\n deltaPodDemand\n blocksToSoldOutSoil\n podRate\n temperature\n deltaTemperature\n season\n }\n siloHourlySnapshots(\n first: 1000\n orderBy: season\n orderDirection: desc\n where: {silo: \"0xd1a0d188e861ed9d15773a2f3574a2e94134ba8f\", season_gte: $from, season_lte: $to}\n ) {\n id\n beanToMaxLpGpPerBdvRatio\n deltaBeanToMaxLpGpPerBdvRatio\n season\n }\n}": typeof types.BeanstalkSeasonsTableDocument, "query SiloSnapshots($first: Int!, $id: Bytes!) {\n siloHourlySnapshots(\n first: $first\n orderBy: season\n orderDirection: desc\n where: {silo_: {id: $id}}\n ) {\n beanToMaxLpGpPerBdvRatio\n deltaBeanMints\n season\n }\n}": typeof types.SiloSnapshotsDocument, "query SiloYields {\n siloYields(\n orderBy: season\n orderDirection: desc\n where: {emaWindow: ROLLING_30_DAY}\n first: 1\n ) {\n beansPerSeasonEMA\n beta\n createdAt\n season\n id\n u\n whitelistedTokens\n emaWindow\n tokenAPYS {\n beanAPY\n stalkAPY\n season\n createdAt\n token\n }\n }\n}": typeof types.SiloYieldsDocument, - "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}": typeof types.AllMarketActivityDocument, + "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt, $listings_podMarketplace: String, $orders_podMarketplace: String) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL, podMarketplace: $listings_podMarketplace}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt, podMarketplace: $orders_podMarketplace}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}": typeof types.AllMarketActivityDocument, "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}": typeof types.AllPodListingsDocument, "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status}\n ) {\n ...PodOrder\n }\n}": typeof types.AllPodOrdersDocument, "query FarmerMarketActivity($first: Int = 1000, $account: String!, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {farmer: $account, createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {farmer: $account, createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(\n first: $first\n where: {and: [{createdAt_gt: $fill_createdAt_gt}, {or: [{fromFarmer: $account}, {toFarmer: $account}]}]}\n ) {\n ...PodFill\n }\n}": typeof types.FarmerMarketActivityDocument, @@ -50,7 +50,7 @@ const documents: Documents = { "query BeanstalkSeasonsTable($from: Int, $to: Int) {\n seasons(\n first: 1000\n orderBy: season\n orderDirection: desc\n where: {season_gte: $from, season_lte: $to}\n ) {\n id\n sunriseBlock\n rewardBeans\n price\n deltaBeans\n raining\n season\n }\n fieldHourlySnapshots(\n first: 1000\n orderBy: season\n orderDirection: desc\n where: {field: \"0xd1a0d188e861ed9d15773a2f3574a2e94134ba8f\", season_gte: $from, season_lte: $to}\n ) {\n id\n caseId\n issuedSoil\n deltaSownBeans\n sownBeans\n deltaPodDemand\n blocksToSoldOutSoil\n podRate\n temperature\n deltaTemperature\n season\n }\n siloHourlySnapshots(\n first: 1000\n orderBy: season\n orderDirection: desc\n where: {silo: \"0xd1a0d188e861ed9d15773a2f3574a2e94134ba8f\", season_gte: $from, season_lte: $to}\n ) {\n id\n beanToMaxLpGpPerBdvRatio\n deltaBeanToMaxLpGpPerBdvRatio\n season\n }\n}": types.BeanstalkSeasonsTableDocument, "query SiloSnapshots($first: Int!, $id: Bytes!) {\n siloHourlySnapshots(\n first: $first\n orderBy: season\n orderDirection: desc\n where: {silo_: {id: $id}}\n ) {\n beanToMaxLpGpPerBdvRatio\n deltaBeanMints\n season\n }\n}": types.SiloSnapshotsDocument, "query SiloYields {\n siloYields(\n orderBy: season\n orderDirection: desc\n where: {emaWindow: ROLLING_30_DAY}\n first: 1\n ) {\n beansPerSeasonEMA\n beta\n createdAt\n season\n id\n u\n whitelistedTokens\n emaWindow\n tokenAPYS {\n beanAPY\n stalkAPY\n season\n createdAt\n token\n }\n }\n}": types.SiloYieldsDocument, - "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}": types.AllMarketActivityDocument, + "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt, $listings_podMarketplace: String, $orders_podMarketplace: String) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL, podMarketplace: $listings_podMarketplace}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt, podMarketplace: $orders_podMarketplace}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}": types.AllMarketActivityDocument, "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}": types.AllPodListingsDocument, "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status}\n ) {\n ...PodOrder\n }\n}": types.AllPodOrdersDocument, "query FarmerMarketActivity($first: Int = 1000, $account: String!, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {farmer: $account, createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {farmer: $account, createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(\n first: $first\n where: {and: [{createdAt_gt: $fill_createdAt_gt}, {or: [{fromFarmer: $account}, {toFarmer: $account}]}]}\n ) {\n ...PodFill\n }\n}": types.FarmerMarketActivityDocument, @@ -119,7 +119,7 @@ export function graphql(source: "query SiloYields {\n siloYields(\n orderBy: /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}"): (typeof documents)["query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}"]; +export function graphql(source: "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt, $listings_podMarketplace: String, $orders_podMarketplace: String) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL, podMarketplace: $listings_podMarketplace}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt, podMarketplace: $orders_podMarketplace}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}"): (typeof documents)["query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt, $listings_podMarketplace: String, $orders_podMarketplace: String) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL, podMarketplace: $listings_podMarketplace}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt, podMarketplace: $orders_podMarketplace}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\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 78aa7d75..98e8681f 100644 --- a/src/generated/gql/pintostalk/graphql.ts +++ b/src/generated/gql/pintostalk/graphql.ts @@ -3413,6 +3413,8 @@ export type Plot = { __typename?: 'Plot'; /** Number of beans spent for each pod, whether through sowing or on the marketplace */ beansPerPod: Scalars['BigInt']['output']; + /** Block number of the most recent plot combination, null if never combined */ + combinedAtBlock?: Maybe; /** Timestamp of entity creation (not sown) */ createdAt: Scalars['BigInt']['output']; /** Transaction hash of when this plot entity was created (not sown) */ @@ -3496,6 +3498,14 @@ export type PlotFilter = { beansPerPod_lte?: InputMaybe; beansPerPod_not?: InputMaybe; beansPerPod_not_in?: InputMaybe>; + combinedAtBlock?: InputMaybe; + combinedAtBlock_gt?: InputMaybe; + combinedAtBlock_gte?: InputMaybe; + combinedAtBlock_in?: InputMaybe>; + combinedAtBlock_lt?: InputMaybe; + combinedAtBlock_lte?: InputMaybe; + combinedAtBlock_not?: InputMaybe; + combinedAtBlock_not_in?: InputMaybe>; createdAt?: InputMaybe; createdAt_gt?: InputMaybe; createdAt_gte?: InputMaybe; @@ -3811,6 +3821,7 @@ export type PlotFilter = { export enum PlotOrderBy { beansPerPod = 'beansPerPod', + combinedAtBlock = 'combinedAtBlock', createdAt = 'createdAt', creationHash = 'creationHash', farmer = 'farmer', @@ -5140,6 +5151,7 @@ export enum PodListingOrderBy { originalPlaceInLine = 'originalPlaceInLine', plot = 'plot', plot__beansPerPod = 'plot__beansPerPod', + plot__combinedAtBlock = 'plot__combinedAtBlock', plot__createdAt = 'plot__createdAt', plot__creationHash = 'plot__creationHash', plot__fieldId = 'plot__fieldId', @@ -14095,6 +14107,8 @@ export type AllMarketActivityQueryVariables = Exact<{ listings_createdAt_gt?: InputMaybe; orders_createdAt_gt?: InputMaybe; fill_createdAt_gt?: InputMaybe; + listings_podMarketplace?: InputMaybe; + orders_podMarketplace?: InputMaybe; }>; @@ -14239,7 +14253,7 @@ export const FieldSnapshotsDocument = {"kind":"Document","definitions":[{"kind": export const BeanstalkSeasonsTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"BeanstalkSeasonsTable"},"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"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seasons"},"arguments":[{"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"}},{"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"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"sunriseBlock"}},{"kind":"Field","name":{"kind":"Name","value":"rewardBeans"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"deltaBeans"}},{"kind":"Field","name":{"kind":"Name","value":"raining"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"Field","name":{"kind":"Name","value":"fieldHourlySnapshots"},"arguments":[{"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"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"field"},"value":{"kind":"StringValue","value":"0xd1a0d188e861ed9d15773a2f3574a2e94134ba8f","block":false}},{"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"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"caseId"}},{"kind":"Field","name":{"kind":"Name","value":"issuedSoil"}},{"kind":"Field","name":{"kind":"Name","value":"deltaSownBeans"}},{"kind":"Field","name":{"kind":"Name","value":"sownBeans"}},{"kind":"Field","name":{"kind":"Name","value":"deltaPodDemand"}},{"kind":"Field","name":{"kind":"Name","value":"blocksToSoldOutSoil"}},{"kind":"Field","name":{"kind":"Name","value":"podRate"}},{"kind":"Field","name":{"kind":"Name","value":"temperature"}},{"kind":"Field","name":{"kind":"Name","value":"deltaTemperature"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}},{"kind":"Field","name":{"kind":"Name","value":"siloHourlySnapshots"},"arguments":[{"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"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"silo"},"value":{"kind":"StringValue","value":"0xd1a0d188e861ed9d15773a2f3574a2e94134ba8f","block":false}},{"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"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"beanToMaxLpGpPerBdvRatio"}},{"kind":"Field","name":{"kind":"Name","value":"deltaBeanToMaxLpGpPerBdvRatio"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}}]}}]} as unknown as DocumentNode; export const SiloSnapshotsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SiloSnapshots"},"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":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Bytes"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloHourlySnapshots"},"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":"season"}},{"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":"silo_"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"beanToMaxLpGpPerBdvRatio"}},{"kind":"Field","name":{"kind":"Name","value":"deltaBeanMints"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}}]}}]} as unknown as DocumentNode; export const SiloYieldsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SiloYields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloYields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"season"}},{"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":"emaWindow"},"value":{"kind":"EnumValue","value":"ROLLING_30_DAY"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"beansPerSeasonEMA"}},{"kind":"Field","name":{"kind":"Name","value":"beta"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"u"}},{"kind":"Field","name":{"kind":"Name","value":"whitelistedTokens"}},{"kind":"Field","name":{"kind":"Name","value":"emaWindow"}},{"kind":"Field","name":{"kind":"Name","value":"tokenAPYS"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"beanAPY"}},{"kind":"Field","name":{"kind":"Name","value":"stalkAPY"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]}}]}}]} as unknown as DocumentNode; -export const AllMarketActivityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllMarketActivity"},"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":"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":"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":"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":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}}}]}}],"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 AllMarketActivityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllMarketActivity"},"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":"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"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"listings_podMarketplace"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orders_podMarketplace"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"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":"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"}},{"kind":"ObjectField","name":{"kind":"Name","value":"podMarketplace"},"value":{"kind":"Variable","name":{"kind":"Name","value":"listings_podMarketplace"}}}]}}],"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":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orders_createdAt_gt"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"podMarketplace"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orders_podMarketplace"}}}]}}],"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":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}}}]}}],"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 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; diff --git a/src/pages/Market.tsx b/src/pages/Market.tsx index 229afc2c..7bfbe64d 100644 --- a/src/pages/Market.tsx +++ b/src/pages/Market.tsx @@ -10,6 +10,7 @@ import ReadMoreAccordion from "@/components/ReadMoreAccordion"; import ScatterChart, { PointClickPayload, PointHoverPayload, ScatterChartRef } from "@/components/charts/ScatterChart"; import { Card } from "@/components/ui/Card"; import { Separator } from "@/components/ui/Separator"; +import { Switch } from "@/components/ui/Switch"; import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import useNavHeight from "@/hooks/display/useNavHeight"; import { useAllMarket } from "@/state/market/useAllMarket"; @@ -213,7 +214,9 @@ export function Market() { const hoverInfoRef = useRef(null); const lastPositionSideRef = useRef<{ isRight: boolean; isAbove: boolean } | null>(null); const navigate = useNavigate(); - const { data, isLoaded } = useAllMarket(); + const [isBeanstalkMarketplace, setIsBeanstalkMarketplace] = useState(false); + const podMarketplaceId = isBeanstalkMarketplace ? "1" : undefined; + const { data, isLoaded } = useAllMarket(podMarketplaceId); const podLine = usePodLine(); const podLineAsNumber = podLine.toNumber() / MILLION; // Chart rounds X max to nearest 10 (not ceil), so we need to match that for validation @@ -1009,7 +1012,11 @@ export function Market() {
-
+
+ Toggle Beanstalk Marketplace + +
+
{!isLoaded && (
diff --git a/src/queries/beanstalk/podmarket/AllMarketActivity.graphql b/src/queries/beanstalk/podmarket/AllMarketActivity.graphql index cd567268..0c14f2a1 100644 --- a/src/queries/beanstalk/podmarket/AllMarketActivity.graphql +++ b/src/queries/beanstalk/podmarket/AllMarketActivity.graphql @@ -7,10 +7,16 @@ query AllMarketActivity( $listings_createdAt_gt: BigInt $orders_createdAt_gt: BigInt $fill_createdAt_gt: BigInt + $listings_podMarketplace: String + $orders_podMarketplace: String ) { podListings( first: $first - where: { createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL } + where: { + createdAt_gt: $listings_createdAt_gt + status_not: FILLED_PARTIAL + podMarketplace: $listings_podMarketplace + } ) { ...PodListing } @@ -19,7 +25,10 @@ query AllMarketActivity( first: $first orderBy: createdAt orderDirection: desc - where: { createdAt_gt: $orders_createdAt_gt } + where: { + createdAt_gt: $orders_createdAt_gt + podMarketplace: $orders_podMarketplace + } ) { ...PodOrder } diff --git a/src/state/market/useAllMarket.ts b/src/state/market/useAllMarket.ts index f09bab3f..8499c9c9 100644 --- a/src/state/market/useAllMarket.ts +++ b/src/state/market/useAllMarket.ts @@ -2,25 +2,30 @@ import { subgraphs } from "@/constants/subgraph"; import { AllMarketActivityDocument } from "@/generated/gql/pintostalk/graphql"; import { useQuery } from "@tanstack/react-query"; import request from "graphql-request"; +import { useMemo } from "react"; import { useChainId } from "wagmi"; import { useQueryKeys } from "../useQueryKeys"; import { useMarketEntities } from "./useMarketEntities"; -export function useAllMarket() { +export function useAllMarket(podMarketplaceId?: string) { const chainId = useChainId(); const { allMarket: queryKey } = useQueryKeys({ chainId }); + const marketQueryKey = useMemo(() => [...queryKey, { podMarketplaceId }], [queryKey, podMarketplaceId]); + const { data, isFetching } = useQuery({ - queryKey, + queryKey: marketQueryKey, queryFn: async () => request(subgraphs[chainId].beanstalk, AllMarketActivityDocument, { listings_createdAt_gt: 0, orders_createdAt_gt: 0, fill_createdAt_gt: 0, first: 1000, + listings_podMarketplace: podMarketplaceId ?? "0", + orders_podMarketplace: podMarketplaceId ?? "0", }), }); - return useMarketEntities(data, isFetching, queryKey); + return useMarketEntities(data, isFetching, marketQueryKey); } From 699bb76eed2cfb7a6cc6f6245c340a0680436266 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Fri, 13 Feb 2026 16:18:03 +0300 Subject: [PATCH 09/22] feat(transfer): add silo, pod, and fertilizer transfer flows --- src/constants/abiSnippets.ts | 87 +++++++ src/constants/address.ts | 4 + src/constants/internalTokens.ts | 8 + src/pages/Transfer.tsx | 11 +- src/pages/transfer/TransferActions.tsx | 35 +++ .../actions/TransferBeanstalkFertilizer.tsx | 115 +++++++++ .../actions/TransferBeanstalkPods.tsx | 145 ++++++++++++ .../actions/TransferBeanstalkSilo.tsx | 113 +++++++++ .../beanstalk-fertilizer/FinalStep.tsx | 39 +++ .../actions/beanstalk-fertilizer/StepOne.tsx | 161 +++++++++++++ .../actions/beanstalk-pods/FinalStep.tsx | 54 +++++ .../actions/beanstalk-pods/StepOne.tsx | 126 ++++++++++ .../actions/beanstalk-pods/StepTwo.tsx | 158 ++++++++++++ .../actions/beanstalk-silo/FinalStep.tsx | 33 +++ .../actions/beanstalk-silo/StepOne.tsx | 90 +++++++ src/state/useBeanstalkGlobalStats.ts | 92 ++++--- src/state/useFarmerBeanstalkRepayment.ts | 224 ++++++++++-------- 17 files changed, 1351 insertions(+), 144 deletions(-) create mode 100644 src/pages/transfer/actions/TransferBeanstalkFertilizer.tsx create mode 100644 src/pages/transfer/actions/TransferBeanstalkPods.tsx create mode 100644 src/pages/transfer/actions/TransferBeanstalkSilo.tsx create mode 100644 src/pages/transfer/actions/beanstalk-fertilizer/FinalStep.tsx create mode 100644 src/pages/transfer/actions/beanstalk-fertilizer/StepOne.tsx create mode 100644 src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx create mode 100644 src/pages/transfer/actions/beanstalk-pods/StepOne.tsx create mode 100644 src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx create mode 100644 src/pages/transfer/actions/beanstalk-silo/FinalStep.tsx create mode 100644 src/pages/transfer/actions/beanstalk-silo/StepOne.tsx diff --git a/src/constants/abiSnippets.ts b/src/constants/abiSnippets.ts index c294ff9a..13083b5c 100644 --- a/src/constants/abiSnippets.ts +++ b/src/constants/abiSnippets.ts @@ -372,6 +372,91 @@ const erc721Enum = [ }, ] as const; +const siloPayback = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "earned", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "getBalanceCombined", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "siloRemaining", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalDistributed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalReceived", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +const barnPayback = [ + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "uint256", name: "id", type: "uint256" }, + ], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "uint256[]", name: "ids", type: "uint256[]" }, + ], + name: "balanceOfFertilized", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "account", type: "address" }, + { internalType: "uint256[]", name: "ids", type: "uint256[]" }, + ], + name: "balanceOfUnfertilized", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "barnRemaining", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "fert", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, +] as const; + export const abiSnippets = { advancedPipe, advancedFarm, @@ -399,4 +484,6 @@ export const abiSnippets = { convert, }, erc721Enum, + siloPayback, + barnPayback, } as const; diff --git a/src/constants/address.ts b/src/constants/address.ts index 43d1e079..dd81e757 100644 --- a/src/constants/address.ts +++ b/src/constants/address.ts @@ -40,3 +40,7 @@ export const WELL_FUNCTION_ADDRESSES: ChainLookup<{ } as const; export const NFT_COLLECTION_1_CONTRACT: HashString = "0xBea5e200a087529AA3B62c460040aE94E3651b76"; + +// TODO: Replace with actual deployed contract addresses +export const SILO_PAYBACK_ADDRESS: HashString = "0x0000000000000000000000000000000000000000"; // urBDV contract address +export const BARN_PAYBACK_ADDRESS: HashString = "0x0000000000000000000000000000000000000000"; // bsFERT contract address diff --git a/src/constants/internalTokens.ts b/src/constants/internalTokens.ts index b9a9ceb1..89283811 100644 --- a/src/constants/internalTokens.ts +++ b/src/constants/internalTokens.ts @@ -36,6 +36,14 @@ export const URBDV: InternalToken = { logoURI: "", // TODO: Add logo if needed }; +export const BSFERT: InternalToken = { + name: "Beanstalk Payback Fertilizer", + symbol: "bsFERT", + decimals: 0, + displayDecimals: 0, + logoURI: "", // TODO: Add bsFERT icon +}; + export const SPROUTS: InternalToken = { name: "Sprouts", symbol: "SPROUTS", diff --git a/src/pages/Transfer.tsx b/src/pages/Transfer.tsx index 57117cf2..4206fd51 100644 --- a/src/pages/Transfer.tsx +++ b/src/pages/Transfer.tsx @@ -4,6 +4,9 @@ import { useParams } from "react-router-dom"; import { useChainId, useConfig } from "wagmi"; import TransferActions from "./transfer/TransferActions"; import TransferAll from "./transfer/actions/TransferAll"; +import TransferBeanstalkFertilizer from "./transfer/actions/TransferBeanstalkFertilizer"; +import TransferBeanstalkPods from "./transfer/actions/TransferBeanstalkPods"; +import TransferBeanstalkSilo from "./transfer/actions/TransferBeanstalkSilo"; import TransferDeposits from "./transfer/actions/TransferDeposits"; import TransferFarmBalance from "./transfer/actions/TransferFarmBalance"; import TransferPods from "./transfer/actions/TransferPods"; @@ -23,7 +26,13 @@ function Transfer() {
{`Move tokens to a different address${currentChain ? ` on ${currentChain.name}.` : "."}`}
- {mode === "all" ? ( + {mode === "beanstalk-silo" ? ( + + ) : mode === "beanstalk-pods" ? ( + + ) : mode === "beanstalk-fertilizer" ? ( + + ) : mode === "all" ? ( ) : mode === "farmbalance" ? ( diff --git a/src/pages/transfer/TransferActions.tsx b/src/pages/transfer/TransferActions.tsx index c4ad9c69..54ad0a3a 100644 --- a/src/pages/transfer/TransferActions.tsx +++ b/src/pages/transfer/TransferActions.tsx @@ -3,6 +3,7 @@ import { TokenValue } from "@/classes/TokenValue"; import { Button } from "@/components/ui/Button"; import IconImage from "@/components/ui/IconImage"; import { useFarmerBalances } from "@/state/useFarmerBalances"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useFarmerField } from "@/state/useFarmerField"; import { useFarmerSilo } from "@/state/useFarmerSilo"; import { usePriceData } from "@/state/usePriceData"; @@ -15,6 +16,7 @@ export default function TransferActions() { const farmerBalance = useFarmerBalances(); const farmerSilo = useFarmerSilo(); const farmerField = useFarmerField(); + const repayment = useFarmerBeanstalkRepayment(); const totalInternalBalance = Array.from(farmerBalance.balances).reduce( (total: TokenValue, tokenBalance) => @@ -75,6 +77,39 @@ export default function TransferActions() {
+ + +
); } diff --git a/src/pages/transfer/actions/TransferBeanstalkFertilizer.tsx b/src/pages/transfer/actions/TransferBeanstalkFertilizer.tsx new file mode 100644 index 00000000..4a78b9ca --- /dev/null +++ b/src/pages/transfer/actions/TransferBeanstalkFertilizer.tsx @@ -0,0 +1,115 @@ +import FlowForm from "@/components/FormFlow"; +import { BARN_PAYBACK_ADDRESS } from "@/constants/address"; +import { beanstalkAbi, beanstalkAddress } from "@/generated/contractHooks"; +import useTransaction from "@/hooks/useTransaction"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { toast } from "sonner"; +import { type Address, encodeFunctionData } from "viem"; +import { useAccount, useChainId } from "wagmi"; +import FinalStep from "./beanstalk-fertilizer/FinalStep"; +import StepOne from "./beanstalk-fertilizer/StepOne"; + +export interface FertilizerTransferItem { + id: bigint; + value: bigint; +} + +export default function TransferBeanstalkFertilizer() { + const account = useAccount(); + const chainId = useChainId(); + const navigate = useNavigate(); + + const [step, setStep] = useState(1); + const [destination, setDestination] = useState(); + const [selectedIds, setSelectedIds] = useState([]); + const [transferNotice, setTransferNotice] = useState(false); + + const repayment = useFarmerBeanstalkRepayment(); + + useEffect(() => { + setTransferNotice(false); + }, [destination]); + + const stepDescription = step === 1 ? "Select Fertilizer IDs and recipient" : "Confirm send"; + + const enableNextStep = + step === 1 + ? selectedIds.length > 0 && selectedIds.every((item) => item.value > 0n) && !!destination && transferNotice + : true; + + const { writeWithEstimateGas, setSubmitting } = useTransaction({ + successCallback: () => { + repayment.refetch(); + navigate("/transfer"); + }, + successMessage: "Transfer success", + errorMessage: "Transfer failed", + }); + + function onSubmit() { + setSubmitting(true); + toast.loading("Transferring..."); + try { + if (!account.address || !destination) return; + + const farmData: `0x${string}`[] = []; + + if (selectedIds.length === 1) { + // Single ID: use transferERC1155 + const transferCall = encodeFunctionData({ + abi: beanstalkAbi, + functionName: "transferERC1155", + args: [BARN_PAYBACK_ADDRESS as Address, destination as Address, selectedIds[0].id, selectedIds[0].value], + }); + farmData.push(transferCall); + } else { + // Multiple IDs: use batchTransferERC1155 + const ids = selectedIds.map((item) => item.id); + const values = selectedIds.map((item) => item.value); + const batchTransferCall = encodeFunctionData({ + abi: beanstalkAbi, + functionName: "batchTransferERC1155", + args: [BARN_PAYBACK_ADDRESS as Address, destination as Address, ids, values], + }); + farmData.push(batchTransferCall); + } + + return writeWithEstimateGas({ + address: beanstalkAddress[chainId as keyof typeof beanstalkAddress], + abi: beanstalkAbi, + functionName: "farm", + args: [farmData], + }); + } catch (e) { + console.error("Transfer beanstalk fertilizer failed", e); + toast.dismiss(); + toast.error("Transfer failed"); + } + } + + return ( + + {step === 1 ? ( + + ) : ( + + )} + + ); +} diff --git a/src/pages/transfer/actions/TransferBeanstalkPods.tsx b/src/pages/transfer/actions/TransferBeanstalkPods.tsx new file mode 100644 index 00000000..e2e60b60 --- /dev/null +++ b/src/pages/transfer/actions/TransferBeanstalkPods.tsx @@ -0,0 +1,145 @@ +import { TokenValue } from "@/classes/TokenValue"; +import FlowForm from "@/components/FormFlow"; +import { beanstalkAbi, beanstalkAddress } from "@/generated/contractHooks"; +import useTransaction from "@/hooks/useTransaction"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { toast } from "sonner"; +import { type Address, encodeFunctionData } from "viem"; +import { useAccount, useChainId } from "wagmi"; +import FinalStep from "./beanstalk-pods/FinalStep"; +import StepOne from "./beanstalk-pods/StepOne"; +import StepTwo from "./beanstalk-pods/StepTwo"; + +export interface PodTransferData { + id: TokenValue; + start: TokenValue; + end: TokenValue; +} + +export default function TransferBeanstalkPods() { + const account = useAccount(); + const chainId = useChainId(); + const navigate = useNavigate(); + + const [step, setStep] = useState(1); + const [destination, setDestination] = useState(); + const [transferData, setTransferData] = useState([]); + const [transferNotice, setTransferNotice] = useState(false); + + const repayment = useFarmerBeanstalkRepayment(); + + useEffect(() => { + setTransferNotice(false); + }, [destination]); + + const stepDescription = () => { + switch (step) { + case 1: + return "Select Plots"; + case 2: + return "Specify amount and address"; + default: + return "Confirm send"; + } + }; + + const enableNextStep = () => { + switch (step) { + case 1: + return transferData.length > 0; + case 2: + if (!!destination && transferNotice) { + if (transferData.length === 1) { + return transferData[0].end.gt(transferData[0].start); + } + return true; + } + return false; + default: + return true; + } + }; + + const { writeWithEstimateGas, setSubmitting } = useTransaction({ + successCallback: () => { + repayment.refetch(); + navigate("/transfer"); + }, + successMessage: "Transfer success", + errorMessage: "Transfer failed", + }); + + function onSubmit() { + setSubmitting(true); + toast.loading("Transferring..."); + try { + if (!account.address || !destination) return; + + const farmData: `0x${string}`[] = []; + + // Plot Transfers โ€” fieldId=1 for Beanstalk Repayment Field + const fieldId = BigInt(1); + const ids: bigint[] = []; + const starts: bigint[] = []; + const ends: bigint[] = []; + for (const plotData of transferData) { + ids.push(plotData.id.toBigInt()); + starts.push(plotData.start.toBigInt()); + ends.push(plotData.end.toBigInt()); + } + const plotTransferCall = encodeFunctionData({ + abi: beanstalkAbi, + functionName: "transferPlots", + args: [ + account.address, + destination as Address, + fieldId, + ids, //plot ids + starts, // starts + ends, // ends + ], + }); + farmData.push(plotTransferCall); + + return writeWithEstimateGas({ + address: beanstalkAddress[chainId as keyof typeof beanstalkAddress], + abi: beanstalkAbi, + functionName: "farm", + args: [farmData], + }); + } catch (e) { + console.error("Transfer beanstalk pods failed", e); + toast.dismiss(); + toast.error("Transfer failed"); + } + } + + return ( + + {step === 1 ? ( + + ) : step === 2 ? ( + + ) : ( + + )} + + ); +} diff --git a/src/pages/transfer/actions/TransferBeanstalkSilo.tsx b/src/pages/transfer/actions/TransferBeanstalkSilo.tsx new file mode 100644 index 00000000..57b7743a --- /dev/null +++ b/src/pages/transfer/actions/TransferBeanstalkSilo.tsx @@ -0,0 +1,113 @@ +import { TokenValue } from "@/classes/TokenValue"; +import FlowForm from "@/components/FormFlow"; +import { SILO_PAYBACK_ADDRESS } from "@/constants/address"; +import { beanstalkAbi, beanstalkAddress } from "@/generated/contractHooks"; +import useTransaction from "@/hooks/useTransaction"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { FarmFromMode, FarmToMode } from "@/utils/types"; +import { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { toast } from "sonner"; +import { type Address, encodeFunctionData } from "viem"; +import { useAccount, useChainId } from "wagmi"; +import FinalStep from "./beanstalk-silo/FinalStep"; +import StepOne from "./beanstalk-silo/StepOne"; + +const URBDV_DECIMALS = 6; + +export default function TransferBeanstalkSilo() { + const account = useAccount(); + const chainId = useChainId(); + const navigate = useNavigate(); + + const [step, setStep] = useState(1); + const [destination, setDestination] = useState(); + const [amount, setAmount] = useState(""); + const [balanceTo, setBalanceTo] = useState(undefined); + const [transferNotice, setTransferNotice] = useState(false); + + const repayment = useFarmerBeanstalkRepayment(); + + useEffect(() => { + setTransferNotice(false); + }, [balanceTo, destination]); + + const stepDescription = step === 1 ? "Specify amount and recipient address" : "Confirm send"; + + const { writeWithEstimateGas, setSubmitting } = useTransaction({ + successCallback: () => { + repayment.refetch(); + navigate("/transfer"); + }, + successMessage: "Transfer success", + errorMessage: "Transfer failed", + }); + + function onSubmit() { + try { + setSubmitting(true); + toast.loading("Transferring..."); + + if (!account.address || !destination || !amount) return; + + const parsedAmount = TokenValue.fromHuman(amount, URBDV_DECIMALS); + if (parsedAmount.eq(0)) return; + + const farmData: `0x${string}`[] = []; + + const transferCall = encodeFunctionData({ + abi: beanstalkAbi, + functionName: "transferToken", + args: [ + SILO_PAYBACK_ADDRESS, + destination as Address, + parsedAmount.toBigInt(), + Number(FarmFromMode.INTERNAL), + Number(balanceTo), + ], + }); + farmData.push(transferCall); + + return writeWithEstimateGas({ + address: beanstalkAddress[chainId as keyof typeof beanstalkAddress], + abi: beanstalkAbi, + functionName: "farm", + args: [farmData], + }); + } catch (e) { + console.error("Transfer Beanstalk Silo failed", e); + toast.dismiss(); + toast.error("Transfer failed"); + } + } + + const numericAmount = Number(amount) || 0; + + return ( + 0 && !!balanceTo && (balanceTo === FarmToMode.INTERNAL ? transferNotice : true) + } + onSubmit={onSubmit} + stepDescription={stepDescription} + > + {step === 1 ? ( + + ) : ( + + )} + + ); +} diff --git a/src/pages/transfer/actions/beanstalk-fertilizer/FinalStep.tsx b/src/pages/transfer/actions/beanstalk-fertilizer/FinalStep.tsx new file mode 100644 index 00000000..2a3e4109 --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-fertilizer/FinalStep.tsx @@ -0,0 +1,39 @@ +import AddressLink from "@/components/AddressLink"; +import { Label } from "@/components/ui/Label"; +import { formatter } from "@/utils/format"; +import { type FertilizerTransferItem } from "../TransferBeanstalkFertilizer"; + +interface FinalStepProps { + destination: string | undefined; + selectedIds: FertilizerTransferItem[]; +} + +export default function FinalStep({ destination, selectedIds }: FinalStepProps) { + if (!destination || selectedIds.length === 0) { + return null; + } + + return ( +
+
+ +
+ {selectedIds.map((item) => ( +
+ {formatter.number(Number(item.value))} + bsFERT + ID {formatter.number(Number(item.id))} +
+ ))} +
+
+
+ + +
+
+ ); +} diff --git a/src/pages/transfer/actions/beanstalk-fertilizer/StepOne.tsx b/src/pages/transfer/actions/beanstalk-fertilizer/StepOne.tsx new file mode 100644 index 00000000..48ad9d06 --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-fertilizer/StepOne.tsx @@ -0,0 +1,161 @@ +import AddressInputField from "@/components/AddressInputField"; +import CheckmarkCircle from "@/components/CheckmarkCircle"; +import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; +import { Button } from "@/components/ui/Button"; +import { Label } from "@/components/ui/Label"; +import { Table, TableBody, TableCell, TableRow } from "@/components/ui/Table"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/ToggleGroup"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { formatter } from "@/utils/format"; +import { AnimatePresence, motion } from "framer-motion"; +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; +import { type FertilizerTransferItem } from "../TransferBeanstalkFertilizer"; + +interface StepOneProps { + selectedIds: FertilizerTransferItem[]; + setSelectedIds: Dispatch>; + destination: string | undefined; + setDestination: Dispatch>; + transferNotice: boolean; + setTransferNotice: Dispatch>; +} + +const variants = { + hidden: { + opacity: 0, + transition: { opacity: { duration: 0.2 } }, + }, + visible: { + opacity: 1, + transition: { opacity: { duration: 0.2 } }, + }, + exit: { + opacity: 0, + transition: { opacity: { duration: 0.2 } }, + }, +}; + +export default function StepOne({ + selectedIds, + setSelectedIds, + destination, + setDestination, + transferNotice, + setTransferNotice, +}: StepOneProps) { + const repayment = useFarmerBeanstalkRepayment(); + const fertilizerIds = repayment.fertilizer.fertilizerIds; + + const [selected, setSelected] = useState(() => selectedIds.map((item) => item.id.toString())); + + useEffect(() => { + const restored: string[] = []; + for (const item of selectedIds) { + if (fertilizerIds.includes(item.id)) { + restored.push(item.id.toString()); + } + } + setSelected(restored); + }, []); + + const handleSelection = useCallback( + (value: string[]) => { + setSelected(value); + + const items: FertilizerTransferItem[] = value + .map((idStr) => { + const id = BigInt(idStr); + // For now, transfer the full balance (value=1 per ERC-1155 token unit) + // The actual balance per ID would come from on-chain data + return { id, value: 1n }; + }) + .filter((item) => fertilizerIds.includes(item.id)); + + setSelectedIds(items); + }, + [fertilizerIds, setSelectedIds], + ); + + const selectAll = useCallback(() => { + const allIds = fertilizerIds.map((id) => id.toString()); + handleSelection(allIds); + }, [fertilizerIds, handleSelection]); + + if (fertilizerIds.length === 0) { + return ( +
+
No Beanstalk Repayment Fertilizer found.
+
+ ); + } + + return ( + +
+ +
+ + + + + + + {fertilizerIds.map((fertId) => ( + + + +
+ +
+
+ Fertilizer ID {formatter.number(Number(fertId))} โ€” 1 bsFERT +
+
+
+
+
+
+ ))} +
+
+
+
+ + + + + + {destination && ( + + + + )} + + +
+ ); +} diff --git a/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx b/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx new file mode 100644 index 00000000..f45e127b --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx @@ -0,0 +1,54 @@ +import podIcon from "@/assets/protocol/Pod.png"; +import AddressLink from "@/components/AddressLink"; +import { Label } from "@/components/ui/Label"; +import { useHarvestableIndex } from "@/state/useFieldData"; +import { formatter } from "@/utils/format"; +import { PodTransferData } from "../TransferBeanstalkPods"; + +interface FinalStepProps { + destination: string | undefined; + transferData: PodTransferData[]; +} + +export default function FinalStep({ destination, transferData }: FinalStepProps) { + const harvestableIndex = useHarvestableIndex(); + + if (!destination || transferData.length === 0) { + return null; + } + + return ( +
+
+ +
+ {transferData.map((transfer) => { + const placeInLine = transfer.id.sub(harvestableIndex); + const podAmount = transfer.end.sub(transfer.start); + + return ( +
+
+ {formatter.number(podAmount)} + Plot + Pods +
+
+ @ + {formatter.number(placeInLine.add(transfer.start))} in Line +
+
+ ); + })} +
+
+
+ + +
+
+ ); +} diff --git a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx new file mode 100644 index 00000000..794e2f7b --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx @@ -0,0 +1,126 @@ +import podIcon from "@/assets/protocol/Pod.png"; +import { TokenValue } from "@/classes/TokenValue"; +import CheckmarkCircle from "@/components/CheckmarkCircle"; +import { Button } from "@/components/ui/Button"; +import { Label } from "@/components/ui/Label"; +import { Table, TableBody, TableCell, TableRow } from "@/components/ui/Table"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/ToggleGroup"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { formatter } from "@/utils/format"; +import { Plot } from "@/utils/types"; +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; +import { PodTransferData } from "../TransferBeanstalkPods"; + +interface StepOneProps { + transferData: PodTransferData[]; + setTransferData: Dispatch>; +} + +export default function StepOne({ transferData, setTransferData }: StepOneProps) { + const [selected, setSelected] = useState(); + const { plots } = useFarmerBeanstalkRepayment().pods; + + useEffect(() => { + const _newPlots: string[] = []; + for (const data of transferData) { + const _plot = plots.find((plot) => plot.index.eq(data.id)); + if (_plot) { + _newPlots.push(_plot.index.toHuman()); + } + } + setSelected(_newPlots); + }, []); + + const handlePlotSelection = useCallback( + (value: string[]) => { + // Update selected plots + setSelected(value); + + // Get selected plots data + const selectedPlots = value + .map((plotIndex) => { + const plot = plots.find((p) => p.index.toHuman() === plotIndex); + return plot; + }) + .filter((plot): plot is Plot => plot !== undefined && !plot.fullyHarvested); + + // If no valid plots selected, clear transfer data + if (selectedPlots.length === 0) { + setTransferData([]); + return; + } + + // Create plot transfer data + const transferData = selectedPlots.map((plot) => { + return { + id: plot.index, + start: TokenValue.ZERO, + end: plot.pods, + }; + }); + + // Update transfer data + setTransferData(transferData); + }, + [plots, setTransferData], + ); + + const selectAllPlots = useCallback(() => { + const plotIndexes = plots.map((plot) => plot.index.toHuman()); + handlePlotSelection(plotIndexes); + }, [plots, handlePlotSelection]); + + return ( + <> +
+ +
+
+ + + + + {plots + .filter((plot) => plot.unharvestablePods?.gt(0)) + .map((plot) => ( + + + +
+ +
+ Pods +
+ {formatter.number(plot.unharvestablePods ?? plot.pods, { + minValue: 0.01, + })}{" "} + Pods +
+
+
+
+
+
+ ))} +
+
+
+
+ + ); +} diff --git a/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx b/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx new file mode 100644 index 00000000..797b529b --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx @@ -0,0 +1,158 @@ +import { TokenValue } from "@/classes/TokenValue"; +import AddressInputField from "@/components/AddressInputField"; +import { ComboInputField } from "@/components/ComboInputField"; +import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; +import PodRangeSelector from "@/components/PodRangeSelector"; +import { Label } from "@/components/ui/Label"; +import { PODS } from "@/constants/internalTokens"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { Plot } from "@/utils/types"; +import { AnimatePresence, motion } from "framer-motion"; +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; +import { PodTransferData } from "../TransferBeanstalkPods"; + +interface StepTwoProps { + transferData: PodTransferData[]; + setTransferData: Dispatch>; + destination: string | undefined; + setDestination: Dispatch>; + transferNotice: boolean; + setTransferNotice: Dispatch>; +} + +export default function StepTwo({ + transferData, + setTransferData, + destination, + setDestination, + transferNotice, + setTransferNotice, +}: StepTwoProps) { + const { plots } = useFarmerBeanstalkRepayment().pods; + const [selectedPlots, setSelectedPlots] = useState([]); + const [amount, setAmount] = useState("0"); + const [range, setRange] = useState<[TokenValue, TokenValue]>([TokenValue.ZERO, TokenValue.ZERO]); + + useEffect(() => { + const _newPlots: Plot[] = []; + for (const data of transferData) { + const _plot = plots.find((plot) => plot.index.eq(data.id)); + if (_plot) { + _newPlots.push(_plot); + } + } + setSelectedPlots(_newPlots); + }, []); + + useEffect(() => { + if (selectedPlots.length === 1) { + const plot = selectedPlots[0]; + setRange([plot.index, plot.index.add(plot.pods)]); + } + }, [selectedPlots]); + + const handleRangeChange = useCallback( + (newRange: TokenValue[]) => { + if (selectedPlots.length === 0 || selectedPlots.length > 1) return; + const plot = selectedPlots[0]; + const newStart = newRange[0]; + const newEnd = newRange[1]; + + const relativeStart = newStart.sub(plot.index); + const relativeEnd = newEnd.sub(plot.index); + + const newData = [ + { + id: plot.index, + start: relativeStart, + end: relativeEnd, + }, + ]; + + const newAmount = newEnd.sub(newStart).toHuman(); + + const batchUpdate = () => { + setRange([newStart, newEnd]); + setTransferData(newData); + setAmount(newAmount); + }; + batchUpdate(); + }, + [selectedPlots, setTransferData], + ); + + const handleAmountChange = useCallback( + (value: string) => { + const newAmount = value; + + if (selectedPlots.length === 0) return; + + const plot = selectedPlots[0]; + const amountValue = TokenValue.fromHuman(newAmount || "0", PODS.decimals); + const newEnd = plot.index.add(amountValue); + + const newStart = plot.index; + const relativeStart = newStart.sub(plot.index); + const relativeEnd = newEnd.sub(plot.index); + + const newData = [ + { + id: plot.index, + start: relativeStart, + end: relativeEnd, + }, + ]; + + const batchUpdate = () => { + setAmount(newAmount); + if (selectedPlots.length === 1) { + setRange([newStart, newEnd]); + setTransferData(newData); + } + }; + batchUpdate(); + }, + [selectedPlots, setTransferData], + ); + + return ( +
+
+ + 1} + altText={selectedPlots.length > 1 ? "Balance:" : "Plot Balance:"} + /> +
+
+ + + + {destination && ( + + + + )} + {" "} +
+ {selectedPlots.length === 1 && ( + + )} +
+ ); +} diff --git a/src/pages/transfer/actions/beanstalk-silo/FinalStep.tsx b/src/pages/transfer/actions/beanstalk-silo/FinalStep.tsx new file mode 100644 index 00000000..80ba03d7 --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-silo/FinalStep.tsx @@ -0,0 +1,33 @@ +import AddressLink from "@/components/AddressLink"; +import { Label } from "@/components/ui/Label"; +import { FarmToMode } from "@/utils/types"; + +interface FinalStepProps { + amount: string; + destination: string | undefined; + balanceTo: FarmToMode | undefined; +} + +export default function FinalStep({ amount, destination, balanceTo }: FinalStepProps) { + return ( +
+
+ +
+ {amount} + urBDV +
+
+
+ + +
+
+ +
+ {balanceTo === FarmToMode.EXTERNAL ? "External Wallet" : "Farm Wallet"} +
+
+
+ ); +} diff --git a/src/pages/transfer/actions/beanstalk-silo/StepOne.tsx b/src/pages/transfer/actions/beanstalk-silo/StepOne.tsx new file mode 100644 index 00000000..abf3582e --- /dev/null +++ b/src/pages/transfer/actions/beanstalk-silo/StepOne.tsx @@ -0,0 +1,90 @@ +import AddressInputField from "@/components/AddressInputField"; +import { ComboInputField } from "@/components/ComboInputField"; +import DestinationBalanceSelect from "@/components/DestinationBalanceSelect"; +import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; +import { Label } from "@/components/ui/Label"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { FarmToMode } from "@/utils/types"; +import { AnimatePresence, motion } from "framer-motion"; +import { Dispatch, SetStateAction } from "react"; + +interface StepOneProps { + amount: string; + setAmount: Dispatch>; + destination: string | undefined; + setDestination: Dispatch>; + balanceTo: FarmToMode | undefined; + setBalanceTo: Dispatch>; + transferNotice: boolean; + setTransferNotice: Dispatch>; +} + +const variants = { + hidden: { + opacity: 0, + transition: { opacity: { duration: 0.2 } }, + }, + visible: { + opacity: 1, + transition: { opacity: { duration: 0.2 } }, + }, + exit: { + opacity: 0, + transition: { opacity: { duration: 0.2 } }, + }, +}; + +export default function StepOne({ + amount, + setAmount, + destination, + setDestination, + balanceTo, + setBalanceTo, + transferNotice, + setTransferNotice, +}: StepOneProps) { + const repayment = useFarmerBeanstalkRepayment(); + const maxAmount = repayment.silo.balance; + + return ( + + + + + + + + + + + {destination && ( + + + + )} + + + + + + + + + ); +} diff --git a/src/state/useBeanstalkGlobalStats.ts b/src/state/useBeanstalkGlobalStats.ts index 4a3518fb..45db6383 100644 --- a/src/state/useBeanstalkGlobalStats.ts +++ b/src/state/useBeanstalkGlobalStats.ts @@ -1,31 +1,12 @@ import { TokenValue } from "@/classes/TokenValue"; +import { abiSnippets } from "@/constants/abiSnippets"; +import { BARN_PAYBACK_ADDRESS, SILO_PAYBACK_ADDRESS } from "@/constants/address"; import { PODS, SPROUTS, URBDV } from "@/constants/internalTokens"; import { defaultQuerySettingsMedium } from "@/constants/query"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import { useCallback, useMemo } from "react"; import { useReadContracts } from "wagmi"; -/** - * ABI snippets for Silo Payback contract global functions - * NOTE: These functions don't exist in the protocol yet - will be indexed from subgraph later - */ -// const siloPaybackGlobalAbi = [ -// { -// inputs: [], -// name: "totalUrBdvDistributed", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [], -// name: "totalPintoPaidOut", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// ] as const; - /** * ABI snippets for Field contract global functions */ @@ -39,20 +20,6 @@ const fieldGlobalAbi = [ }, ] as const; -/** - * ABI snippets for Barn Payback contract global functions - * NOTE: This function doesn't exist in the protocol yet - will be indexed from subgraph later - */ -// const barnPaybackGlobalAbi = [ -// { -// inputs: [], -// name: "totalUnfertilizedSprouts", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// ] as const; - /** * Interface for the global Beanstalk statistics data */ @@ -61,6 +28,8 @@ export interface BeanstalkGlobalStatsData { totalPodsInRepaymentField: TokenValue; totalUnfertilizedSprouts: TokenValue; totalPintoPaidOut: TokenValue; + siloRemaining: TokenValue; + barnRemaining: TokenValue; isLoading: boolean; isError: boolean; refetch: () => Promise; @@ -73,10 +42,10 @@ const BEANSTALK_REPAYMENT_FIELD_ID = 1n; * Hook for fetching global Beanstalk repayment statistics * * This hook fetches protocol-wide statistics: - * - Total urBDV distributed across all holders (TODO: from subgraph) + * - Silo Payback: siloRemaining() and totalDistributed() from Silo_Payback contract * - Total pods in the repayment field (fieldId=1) - * - Total unfertilized sprouts (TODO: from subgraph) - * - Total Pinto paid out to holders (TODO: from subgraph) + * - Barn Payback: barnRemaining() from Barn_Payback contract + * - Total Pinto paid out to holders (TODO: calculated total) * * Uses a 5-minute stale time for more frequent updates of global stats * @@ -94,11 +63,23 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { functionName: "totalPods", args: [BEANSTALK_REPAYMENT_FIELD_ID], }, - // TODO: These functions don't exist in the protocol yet - // Will be indexed from subgraph later: - // - totalUrBdvDistributed - // - totalUnfertilizedSprouts - // - totalPintoPaidOut + // Silo Payback global stats + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "siloRemaining", + }, + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "totalDistributed", + }, + // Barn Payback global stats + { + address: BARN_PAYBACK_ADDRESS, + abi: abiSnippets.barnPayback, + functionName: "barnRemaining", + }, ], allowFailure: true, query: { @@ -106,18 +87,33 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { }, }); - // Process global data + // Process global data โ€” defaults to ZERO on error const globalData = useMemo(() => { + if (globalQuery.isError) { + return { + totalUrBdvDistributed: TokenValue.ZERO, + totalPodsInRepaymentField: TokenValue.ZERO, + totalUnfertilizedSprouts: TokenValue.ZERO, + totalPintoPaidOut: TokenValue.ZERO, + siloRemaining: TokenValue.ZERO, + barnRemaining: TokenValue.ZERO, + }; + } + const totalPodsInRepaymentField = globalQuery.data?.[0]?.result; + const siloRemainingResult = globalQuery.data?.[1]?.result; + const totalDistributedResult = globalQuery.data?.[2]?.result; + const barnRemainingResult = globalQuery.data?.[3]?.result; return { - // TODO: These will come from subgraph later - totalUrBdvDistributed: TokenValue.fromBlockchain(0n, URBDV.decimals), + totalUrBdvDistributed: TokenValue.fromBlockchain(totalDistributedResult ?? 0n, URBDV.decimals), totalPodsInRepaymentField: TokenValue.fromBlockchain(totalPodsInRepaymentField ?? 0n, PODS.decimals), - totalUnfertilizedSprouts: TokenValue.fromBlockchain(0n, SPROUTS.decimals), + totalUnfertilizedSprouts: TokenValue.fromBlockchain(barnRemainingResult ?? 0n, SPROUTS.decimals), totalPintoPaidOut: TokenValue.fromBlockchain(0n, URBDV.decimals), + siloRemaining: TokenValue.fromBlockchain(siloRemainingResult ?? 0n, URBDV.decimals), + barnRemaining: TokenValue.fromBlockchain(barnRemainingResult ?? 0n, SPROUTS.decimals), }; - }, [globalQuery.data]); + }, [globalQuery.data, globalQuery.isError]); // Refetch function const refetch = useCallback(async () => { diff --git a/src/state/useFarmerBeanstalkRepayment.ts b/src/state/useFarmerBeanstalkRepayment.ts index 28bc670f..f11dc1be 100644 --- a/src/state/useFarmerBeanstalkRepayment.ts +++ b/src/state/useFarmerBeanstalkRepayment.ts @@ -1,6 +1,7 @@ import { TokenValue } from "@/classes/TokenValue"; -import { ZERO_ADDRESS } from "@/constants/address"; -import { PODS } from "@/constants/internalTokens"; +import { abiSnippets } from "@/constants/abiSnippets"; +import { BARN_PAYBACK_ADDRESS, SILO_PAYBACK_ADDRESS, ZERO_ADDRESS } from "@/constants/address"; +import { BSFERT, PODS } from "@/constants/internalTokens"; import { defaultQuerySettings } from "@/constants/query"; import { beanstalkAbi } from "@/generated/contractHooks"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; @@ -9,81 +10,14 @@ import { useCallback, useMemo } from "react"; import { toHex } from "viem"; import { useAccount, useReadContracts } from "wagmi"; -/** - * ABI snippets for Silo Payback contract functions - * NOTE: These functions don't exist in the protocol yet - will be indexed from subgraph later - */ -// const siloPaybackAbi = [ -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "balanceOfUrBdv", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "earnedUrBdv", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "totalDistributedToAccount", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "totalReceivedByAccount", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// ] as const; - -/** - * ABI snippets for Barn Payback contract functions - * NOTE: These functions don't exist in the protocol yet - will be indexed from subgraph later - */ -// const barnPaybackAbi = [ -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "balanceOfFertilizer", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "balanceOfSprouts", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [{ internalType: "address", name: "account", type: "address" }], -// name: "balanceOfFertilized", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// { -// inputs: [], -// name: "humidity", -// outputs: [{ internalType: "uint256", name: "", type: "uint256" }], -// stateMutability: "view", -// type: "function", -// }, -// ] as const; - /** * Interface for silo payback data */ interface SiloPaybackData { - balance: TokenValue; + balance: TokenValue; // getBalanceCombined(account) - total urBDV balance + earned: TokenValue; // earned(account) - earned but unclaimed + totalDistributed: TokenValue; // totalDistributed() - total distributed + totalReceived: TokenValue; // totalReceived() - total received } /** @@ -98,7 +32,10 @@ interface PodsData { * Interface for fertilizer data */ interface FertilizerData { - balance: TokenValue; + balance: TokenValue; // Total bsFERT balance + fertilized: TokenValue; // balanceOfFertilized(account, ids) + unfertilized: TokenValue; // balanceOfUnfertilized(account, ids) + fertilizerIds: bigint[]; // Fertilizer IDs owned by the user } /** @@ -115,16 +52,14 @@ export interface FarmerBeanstalkRepaymentData { // Token decimals for urBDV (same as BEAN - 6 decimals) const URBDV_DECIMALS = 6; -// Token decimals for fertilizer amounts -const FERTILIZER_DECIMALS = 6; /** * Hook for fetching farmer-specific Beanstalk repayment data * * This hook fetches: - * - Silo payback data (TODO: from subgraph - urBDV balance) + * - Silo payback data from Silo_Payback contract (earned, balance, totalDistributed, totalReceived) * - Pods data from repayment field (fieldId=1) - from on-chain - * - Fertilizer data (TODO: from subgraph) + * - Fertilizer data from Barn_Payback contract (fertilized, unfertilized balances) * * @returns FarmerBeanstalkRepaymentData with all farmer obligations data */ @@ -157,16 +92,97 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { }, }); - // TODO: Silo payback data will come from subgraph - // Functions don't exist in protocol: balanceOfUrBdv, earnedUrBdv, totalDistributedToAccount, totalReceivedByAccount + // Query for Barn Payback (bsFERT) data from Barn_Payback contract + // balanceOfFertilized and balanceOfUnfertilized require fertilizer IDs. + // We pass empty arrays initially; these will return 0 until IDs are populated. + const fertilizerIds: bigint[] = []; + + const fertilizerQuery = useReadContracts({ + contracts: [ + { + address: BARN_PAYBACK_ADDRESS, + abi: abiSnippets.barnPayback, + functionName: "balanceOfFertilized", + args: [farmerAddress, fertilizerIds], + }, + { + address: BARN_PAYBACK_ADDRESS, + abi: abiSnippets.barnPayback, + functionName: "balanceOfUnfertilized", + args: [farmerAddress, fertilizerIds], + }, + ], + allowFailure: true, + query: { + ...defaultQuerySettings, + }, + }); + + // Query for Silo Payback data from Silo_Payback contract + const siloQuery = useReadContracts({ + contracts: [ + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "earned", + args: [farmerAddress], + }, + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "getBalanceCombined", + args: [farmerAddress], + }, + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "totalDistributed", + }, + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "totalReceived", + }, + ], + allowFailure: true, + query: { + ...defaultQuerySettings, + }, + }); + + // Process Silo Payback data โ€” defaults to ZERO on error const siloData = useMemo((): SiloPaybackData => { + if (siloQuery.isError) { + return { + balance: TokenValue.ZERO, + earned: TokenValue.ZERO, + totalDistributed: TokenValue.ZERO, + totalReceived: TokenValue.ZERO, + }; + } + + const earnedResult = siloQuery.data?.[0]?.result; + const balanceResult = siloQuery.data?.[1]?.result; + const totalDistributedResult = siloQuery.data?.[2]?.result; + const totalReceivedResult = siloQuery.data?.[3]?.result; + return { - balance: TokenValue.fromBlockchain(0n, URBDV_DECIMALS), + balance: TokenValue.fromBlockchain(balanceResult ?? 0n, URBDV_DECIMALS), + earned: TokenValue.fromBlockchain(earnedResult ?? 0n, URBDV_DECIMALS), + totalDistributed: TokenValue.fromBlockchain(totalDistributedResult ?? 0n, URBDV_DECIMALS), + totalReceived: TokenValue.fromBlockchain(totalReceivedResult ?? 0n, URBDV_DECIMALS), }; - }, []); + }, [siloQuery.data, siloQuery.isError]); - // Process pods data - these functions exist in protocol + // Process pods data โ€” defaults to ZERO on error const podsData = useMemo((): PodsData => { + if (podsQuery.isError) { + return { + plots: [], + totalPods: TokenValue.ZERO, + }; + } + const plotsResult = podsQuery.data?.[0]?.result as readonly { index: bigint; pods: bigint }[] | undefined; const totalPodsResult = podsQuery.data?.[1]?.result; @@ -189,25 +205,43 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { plots, totalPods: TokenValue.fromBlockchain(totalPodsResult ?? 0n, PODS.decimals), }; - }, [podsQuery.data]); + }, [podsQuery.data, podsQuery.isError]); - // TODO: Fertilizer data will come from subgraph - // Functions don't exist in protocol: balanceOfFertilizer, balanceOfSprouts, balanceOfFertilized - // humidity() exists as getCurrentHumidity() but not needed for now + // Process Barn Payback (bsFERT) data โ€” defaults to ZERO on error const fertilizerData = useMemo((): FertilizerData => { + if (fertilizerQuery.isError) { + return { + balance: TokenValue.ZERO, + fertilized: TokenValue.ZERO, + unfertilized: TokenValue.ZERO, + fertilizerIds, + }; + } + + const fertilizedResult = fertilizerQuery.data?.[0]?.result; + const unfertilizedResult = fertilizerQuery.data?.[1]?.result; + + const fertilized = TokenValue.fromBlockchain(fertilizedResult ?? 0n, BSFERT.decimals); + const unfertilized = TokenValue.fromBlockchain(unfertilizedResult ?? 0n, BSFERT.decimals); + // Total balance is the sum of fertilized + unfertilized + const balance = fertilized.add(unfertilized); + return { - balance: TokenValue.fromBlockchain(0n, FERTILIZER_DECIMALS), + balance, + fertilized, + unfertilized, + fertilizerIds, }; - }, []); + }, [fertilizerQuery.data, fertilizerQuery.isError, fertilizerIds]); - // Refetch pods query (only one that works) + // Refetch all queries const refetch = useCallback(async () => { - await podsQuery.refetch(); - }, [podsQuery.refetch]); + await Promise.all([siloQuery.refetch(), podsQuery.refetch(), fertilizerQuery.refetch()]); + }, [siloQuery.refetch, podsQuery.refetch, fertilizerQuery.refetch]); - // Loading and error states only from pods query - const isLoading = podsQuery.isLoading; - const isError = podsQuery.isError; + // Loading and error states from all queries + const isLoading = siloQuery.isLoading || podsQuery.isLoading || fertilizerQuery.isLoading; + const isError = siloQuery.isError || podsQuery.isError || fertilizerQuery.isError; return useMemo( () => ({ From fa1c8d412b4b26f446256ecde312bcfea9fed381 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Fri, 13 Feb 2026 18:36:51 +0300 Subject: [PATCH 10/22] feat(transfer): update pod and fertilizer selection and transfer flows --- src/assets/protocol/Fertilizer.svg | 24 ++ src/components/FertilizerCard.tsx | 64 +++++ src/components/PintoAssetTransferNotice.tsx | 21 +- .../actions/TransferBeanstalkPods.tsx | 21 +- .../actions/beanstalk-fertilizer/StepOne.tsx | 186 +++++++++----- .../actions/beanstalk-pods/FinalStep.tsx | 52 ++-- .../actions/beanstalk-pods/StepOne.tsx | 242 +++++++++++------- .../actions/beanstalk-pods/StepTwo.tsx | 125 +-------- src/state/useFarmerBeanstalkRepayment.ts | 2 +- 9 files changed, 416 insertions(+), 321 deletions(-) create mode 100644 src/assets/protocol/Fertilizer.svg create mode 100644 src/components/FertilizerCard.tsx diff --git a/src/assets/protocol/Fertilizer.svg b/src/assets/protocol/Fertilizer.svg new file mode 100644 index 00000000..14d969f1 --- /dev/null +++ b/src/assets/protocol/Fertilizer.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/FertilizerCard.tsx b/src/components/FertilizerCard.tsx new file mode 100644 index 00000000..f1dc2117 --- /dev/null +++ b/src/components/FertilizerCard.tsx @@ -0,0 +1,64 @@ +import fertilizerIcon from "@/assets/protocol/Fertilizer.svg"; +import CheckmarkCircle from "@/components/CheckmarkCircle"; +import IconImage from "@/components/ui/IconImage"; +import { Input } from "@/components/ui/Input"; +import { formatter } from "@/utils/format"; + +interface FertilizerCardProps { + fertId: bigint; + amount: string; + isSelected: boolean; + maxBalance: bigint; + sprouts: string; + humidity: string; + onToggleSelection: (fertId: bigint) => void; + onAmountChange: (fertId: bigint, value: string, maxBalance: bigint) => void; +} + +export default function FertilizerCard({ + fertId, + amount, + isSelected, + maxBalance, + sprouts, + humidity, + onToggleSelection, + onAmountChange, +}: FertilizerCardProps) { + return ( +
+ {/* Checkbox */} +
onToggleSelection(fertId)}> + +
+ + {/* Fertilizer icon */} +
+ +
+ + {/* Fertilizer info */} +
+
{formatter.number(Number(fertId))} FERTILIZER
+
+ Sprouts: {sprouts} ยท Humidity: {humidity} +
+
+ + {/* Amount input */} +
+ onAmountChange(fertId, e.target.value, maxBalance)} + outlined={true} + containerClassName="border border-pinto-green-4 focus-within:border-pinto-green-4" + min="0" + max={maxBalance.toString()} + step="1" + /> +
+
+ ); +} diff --git a/src/components/PintoAssetTransferNotice.tsx b/src/components/PintoAssetTransferNotice.tsx index 9ddd7667..1e5528e4 100644 --- a/src/components/PintoAssetTransferNotice.tsx +++ b/src/components/PintoAssetTransferNotice.tsx @@ -76,9 +76,26 @@ export default function PintoAssetTransferNotice({ animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3, ease: "easeInOut" }} - className="text-2xl" + className="flex flex-col gap-8" > - Note: be sure recipient address is intended. +

Note: be sure recipient address is intended.

+
+ { + if (checked !== "indeterminate") { + setTransferNotice(checked); + } else { + setTransferNotice(false); + } + }} + /> + +
)} diff --git a/src/pages/transfer/actions/TransferBeanstalkPods.tsx b/src/pages/transfer/actions/TransferBeanstalkPods.tsx index e2e60b60..e5af15a9 100644 --- a/src/pages/transfer/actions/TransferBeanstalkPods.tsx +++ b/src/pages/transfer/actions/TransferBeanstalkPods.tsx @@ -39,7 +39,7 @@ export default function TransferBeanstalkPods() { case 1: return "Select Plots"; case 2: - return "Specify amount and address"; + return "Enter address"; default: return "Confirm send"; } @@ -50,13 +50,7 @@ export default function TransferBeanstalkPods() { case 1: return transferData.length > 0; case 2: - if (!!destination && transferNotice) { - if (transferData.length === 1) { - return transferData[0].end.gt(transferData[0].start); - } - return true; - } - return false; + return !!destination && transferNotice; default: return true; } @@ -92,14 +86,7 @@ export default function TransferBeanstalkPods() { const plotTransferCall = encodeFunctionData({ abi: beanstalkAbi, functionName: "transferPlots", - args: [ - account.address, - destination as Address, - fieldId, - ids, //plot ids - starts, // starts - ends, // ends - ], + args: [account.address, destination as Address, fieldId, ids, starts, ends], }); farmData.push(plotTransferCall); @@ -130,8 +117,6 @@ export default function TransferBeanstalkPods() { ) : step === 2 ? ( (() => selectedIds.map((item) => item.id.toString())); + // TODO: Get actual balance per fertilizer ID from contract + // For now, mock balance of 1 bsFERT per ID + const getFertilizerBalance = useCallback((fertId: bigint): bigint => { + // Mock: Each fertilizer ID has 1 bsFERT + return 1n; + }, []); + + // Local state for amount inputs per fertilizer ID + const [amounts, setAmounts] = useState>(() => { + const initial: Record = {}; + for (const item of selectedIds) { + initial[item.id.toString()] = item.value.toString(); + } + return initial; + }); - useEffect(() => { - const restored: string[] = []; + // Track which fertilizers are selected (have amount > 0) + const [selected, setSelected] = useState>(() => { + const initial = new Set(); for (const item of selectedIds) { - if (fertilizerIds.includes(item.id)) { - restored.push(item.id.toString()); + if (item.value > 0n) { + initial.add(item.id.toString()); } } - setSelected(restored); - }, []); + return initial; + }); + + const toggleSelection = useCallback( + (fertId: bigint) => { + const idStr = fertId.toString(); + setSelected((prev) => { + const newSet = new Set(prev); + if (newSet.has(idStr)) { + newSet.delete(idStr); + // Clear amount when deselecting + setAmounts((prevAmounts) => ({ ...prevAmounts, [idStr]: "" })); + setSelectedIds((prev) => prev.filter((item) => item.id !== fertId)); + } else { + newSet.add(idStr); + // Set default amount of 1 when selecting + setAmounts((prevAmounts) => ({ ...prevAmounts, [idStr]: "1" })); + setSelectedIds((prev) => [...prev, { id: fertId, value: 1n }]); + } + return newSet; + }); + }, + [setSelectedIds], + ); - const handleSelection = useCallback( - (value: string[]) => { - setSelected(value); + const handleAmountChange = useCallback( + (fertId: bigint, value: string, maxBalance: bigint) => { + const idStr = fertId.toString(); - const items: FertilizerTransferItem[] = value - .map((idStr) => { - const id = BigInt(idStr); - // For now, transfer the full balance (value=1 per ERC-1155 token unit) - // The actual balance per ID would come from on-chain data - return { id, value: 1n }; - }) - .filter((item) => fertilizerIds.includes(item.id)); + // Validate against max balance + let numValue = value === "" ? 0n : BigInt(value); + if (numValue > maxBalance) { + numValue = maxBalance; + } + + const displayValue = numValue === 0n ? "" : numValue.toString(); + setAmounts((prev) => ({ ...prev, [idStr]: displayValue })); - setSelectedIds(items); + // Update selectedIds with the new amount + setSelectedIds((prev) => { + const existing = prev.find((item) => item.id === fertId); + if (numValue === 0n) { + // Remove if amount is 0 + setSelected((prevSelected) => { + const newSet = new Set(prevSelected); + newSet.delete(idStr); + return newSet; + }); + return prev.filter((item) => item.id !== fertId); + } + // Auto-select if amount is entered + setSelected((prevSelected) => new Set(prevSelected).add(idStr)); + if (existing) { + // Update existing + return prev.map((item) => (item.id === fertId ? { ...item, value: numValue } : item)); + } + // Add new + return [...prev, { id: fertId, value: numValue }]; + }); }, - [fertilizerIds, setSelectedIds], + [setSelectedIds], ); const selectAll = useCallback(() => { - const allIds = fertilizerIds.map((id) => id.toString()); - handleSelection(allIds); - }, [fertilizerIds, handleSelection]); + const newAmounts: Record = {}; + const newSelectedIds: FertilizerTransferItem[] = []; + const newSelected = new Set(); + + for (const fertId of fertilizerIds) { + const idStr = fertId.toString(); + // For now, default to 1 bsFERT per ID when selecting all + // TODO: Use actual balance per ID from on-chain data + newAmounts[idStr] = "1"; + newSelectedIds.push({ id: fertId, value: 1n }); + newSelected.add(idStr); + } + + setAmounts(newAmounts); + setSelectedIds(newSelectedIds); + setSelected(newSelected); + }, [fertilizerIds, setSelectedIds]); if (fertilizerIds.length === 0) { return ( @@ -96,44 +169,37 @@ export default function StepOne({ className="font-[340] sm:pr-0 text-[1rem] sm:text-[1.25rem] text-pinto-green-4 bg-transparent hover:underline hover:bg-transparent" onClick={selectAll} > - Select all Fertilizer + Select all
- - - - - {fertilizerIds.map((fertId) => ( - - - -
- -
-
- Fertilizer ID {formatter.number(Number(fertId))} โ€” 1 bsFERT -
-
-
-
-
-
- ))} -
-
-
+ +
+ {fertilizerIds.map((fertId) => { + const idStr = fertId.toString(); + const amount = amounts[idStr] || ""; + const isSelected = selected.has(idStr); + const maxBalance = getFertilizerBalance(fertId); + // TODO: Replace with actual sprouts and humidity data from on-chain + const sprouts = "407,287"; + const humidity = "500%"; + + return ( + + ); + })} +
diff --git a/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx b/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx index f45e127b..1e6b7d1b 100644 --- a/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx +++ b/src/pages/transfer/actions/beanstalk-pods/FinalStep.tsx @@ -3,6 +3,8 @@ import AddressLink from "@/components/AddressLink"; import { Label } from "@/components/ui/Label"; import { useHarvestableIndex } from "@/state/useFieldData"; import { formatter } from "@/utils/format"; +import { computeSummaryRange } from "@/utils/podTransferUtils"; +import { useMemo } from "react"; import { PodTransferData } from "../TransferBeanstalkPods"; interface FinalStepProps { @@ -13,36 +15,42 @@ interface FinalStepProps { export default function FinalStep({ destination, transferData }: FinalStepProps) { const harvestableIndex = useHarvestableIndex(); - if (!destination || transferData.length === 0) { + const summary = useMemo(() => { + if (transferData.length === 0) return null; + return computeSummaryRange(transferData, harvestableIndex); + }, [transferData, harvestableIndex]); + + if (!destination || !summary) { return null; } + const { totalPods, placeInLineStart, placeInLineEnd } = summary; + const isSinglePlot = transferData.length === 1; + return (
- {transferData.map((transfer) => { - const placeInLine = transfer.id.sub(harvestableIndex); - const podAmount = transfer.end.sub(transfer.start); - - return ( -
-
- {formatter.number(podAmount)} - Plot - Pods -
-
- @ - {formatter.number(placeInLine.add(transfer.start))} in Line -
-
- ); - })} +
+
+ {formatter.number(totalPods)} + Plot + Pods +
+
+ {isSinglePlot ? ( + <> + @ + {formatter.number(placeInLineStart)} in Line + + ) : ( + + between {formatter.number(placeInLineStart)} - {formatter.number(placeInLineEnd)} in Line + + )} +
+
diff --git a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx index 794e2f7b..6c898322 100644 --- a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx +++ b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx @@ -1,14 +1,11 @@ -import podIcon from "@/assets/protocol/Pod.png"; -import { TokenValue } from "@/classes/TokenValue"; -import CheckmarkCircle from "@/components/CheckmarkCircle"; -import { Button } from "@/components/ui/Button"; -import { Label } from "@/components/ui/Label"; -import { Table, TableBody, TableCell, TableRow } from "@/components/ui/Table"; -import { ToggleGroup, ToggleGroupItem } from "@/components/ui/ToggleGroup"; +import PodLineGraph from "@/components/PodLineGraph"; +import { MultiSlider } from "@/components/ui/Slider"; import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { useHarvestableIndex } from "@/state/useFieldData"; import { formatter } from "@/utils/format"; +import { computeTransferData, offsetToAbsoluteIndex } from "@/utils/podTransferUtils"; import { Plot } from "@/utils/types"; -import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; +import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { PodTransferData } from "../TransferBeanstalkPods"; interface StepOneProps { @@ -16,111 +13,164 @@ interface StepOneProps { setTransferData: Dispatch>; } +function sortPlotsByIndex(plots: Plot[]): Plot[] { + return [...plots].sort((a, b) => a.index.sub(b.index).toNumber()); +} + export default function StepOne({ transferData, setTransferData }: StepOneProps) { - const [selected, setSelected] = useState(); const { plots } = useFarmerBeanstalkRepayment().pods; + const harvestableIndex = useHarvestableIndex(); + + const [selectedPlots, setSelectedPlots] = useState([]); + const [podRange, setPodRange] = useState<[number, number]>([0, 0]); + const mountedRef = useRef(false); + + // Restore selection from existing transferData on mount useEffect(() => { - const _newPlots: string[] = []; - for (const data of transferData) { - const _plot = plots.find((plot) => plot.index.eq(data.id)); - if (_plot) { - _newPlots.push(_plot.index.toHuman()); - } + if (mountedRef.current) return; + mountedRef.current = true; + if (transferData.length === 0) return; + const restoredPlots = transferData + .map((data) => plots.find((p) => p.index.eq(data.id))) + .filter((p): p is Plot => p !== undefined); + if (restoredPlots.length > 0) { + const sorted = sortPlotsByIndex(restoredPlots); + setSelectedPlots(sorted); + const total = sorted.reduce((sum, p) => sum + p.pods.toNumber(), 0); + setPodRange([0, total]); } - setSelected(_newPlots); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // Total pods across selected plots + const totalPods = useMemo(() => { + return selectedPlots.reduce((sum, p) => sum + p.pods.toNumber(), 0); + }, [selectedPlots]); + + const amount = podRange[1] - podRange[0]; + + const selectedPlotIndices = useMemo(() => selectedPlots.map((p) => p.index.toHuman()), [selectedPlots]); + + const positionInfo = useMemo(() => { + if (selectedPlots.length === 0) return null; + const first = selectedPlots[0]; + const last = selectedPlots[selectedPlots.length - 1]; + return { + start: first.index.sub(harvestableIndex), + end: last.index.add(last.pods).sub(harvestableIndex), + }; + }, [selectedPlots, harvestableIndex]); + + const selectedPodRange = useMemo(() => { + if (selectedPlots.length === 0) return undefined; + return { + start: offsetToAbsoluteIndex(podRange[0], selectedPlots), + end: offsetToAbsoluteIndex(podRange[1], selectedPlots), + }; + }, [selectedPlots, podRange]); + const handlePlotSelection = useCallback( - (value: string[]) => { - // Update selected plots - setSelected(value); - - // Get selected plots data - const selectedPlots = value - .map((plotIndex) => { - const plot = plots.find((p) => p.index.toHuman() === plotIndex); - return plot; - }) - .filter((plot): plot is Plot => plot !== undefined && !plot.fullyHarvested); - - // If no valid plots selected, clear transfer data - if (selectedPlots.length === 0) { + (newPlots: Plot[]) => { + const sorted = sortPlotsByIndex(newPlots); + setSelectedPlots(sorted); + + if (sorted.length > 0) { + const newTotal = sorted.reduce((sum, p) => sum + p.pods.toNumber(), 0); + setPodRange([0, newTotal]); + setTransferData(computeTransferData(sorted, [0, newTotal])); + } else { + setPodRange([0, 0]); setTransferData([]); + } + }, + [setTransferData], + ); + + const handlePlotGroupSelect = useCallback( + (plotIndices: string[]) => { + const groupSet = new Set(plotIndices); + const plotsInGroup = plots.filter((p) => groupSet.has(p.index.toHuman())); + if (plotsInGroup.length === 0) return; + + const selectedSet = new Set(selectedPlots.map((p) => p.index.toHuman())); + const allSelected = plotIndices.every((idx) => selectedSet.has(idx)); + + if (allSelected) { + handlePlotSelection(selectedPlots.filter((p) => !groupSet.has(p.index.toHuman()))); return; } - // Create plot transfer data - const transferData = selectedPlots.map((plot) => { - return { - id: plot.index, - start: TokenValue.ZERO, - end: plot.pods, - }; - }); - - // Update transfer data - setTransferData(transferData); + const newPlots = [...selectedPlots]; + for (const plotToAdd of plotsInGroup) { + if (!selectedSet.has(plotToAdd.index.toHuman())) { + newPlots.push(plotToAdd); + } + } + handlePlotSelection(newPlots); }, - [plots, setTransferData], + [plots, selectedPlots, handlePlotSelection], ); - const selectAllPlots = useCallback(() => { - const plotIndexes = plots.map((plot) => plot.index.toHuman()); - handlePlotSelection(plotIndexes); - }, [plots, handlePlotSelection]); + const handlePodRangeChange = useCallback( + (value: number[]) => { + const newRange: [number, number] = [value[0], value[1]]; + setPodRange(newRange); + setTransferData(computeTransferData(selectedPlots, newRange)); + }, + [selectedPlots, setTransferData], + ); return ( - <> -
- -
-
- - - - - {plots - .filter((plot) => plot.unharvestablePods?.gt(0)) - .map((plot) => ( - - - -
- -
- Pods -
- {formatter.number(plot.unharvestablePods ?? plot.pods, { - minValue: 0.01, - })}{" "} - Pods -
-
-
-
-
-
- ))} -
-
-
+
+
+ + + {positionInfo && ( +
+

+ {positionInfo.start.toHuman("short")} - {positionInfo.end.toHuman("short")} +

+
+ )}
- + + {totalPods > 0 && ( +
+

Total Pods to send:

+

{formatter.noDec(amount)} Pods

+
+ )} + + {selectedPlots.length > 0 && ( +
+
+

Select Pods

+
+

{formatter.noDec(podRange[0])}

+
+ {totalPods > 0 && ( + + )} +
+

{formatter.noDec(podRange[1])}

+
+
+
+ )} +
); } diff --git a/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx b/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx index 797b529b..86237f98 100644 --- a/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx +++ b/src/pages/transfer/actions/beanstalk-pods/StepTwo.tsx @@ -1,135 +1,19 @@ -import { TokenValue } from "@/classes/TokenValue"; import AddressInputField from "@/components/AddressInputField"; -import { ComboInputField } from "@/components/ComboInputField"; import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; -import PodRangeSelector from "@/components/PodRangeSelector"; import { Label } from "@/components/ui/Label"; -import { PODS } from "@/constants/internalTokens"; -import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; -import { Plot } from "@/utils/types"; import { AnimatePresence, motion } from "framer-motion"; -import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; -import { PodTransferData } from "../TransferBeanstalkPods"; +import { Dispatch, SetStateAction } from "react"; interface StepTwoProps { - transferData: PodTransferData[]; - setTransferData: Dispatch>; destination: string | undefined; setDestination: Dispatch>; transferNotice: boolean; setTransferNotice: Dispatch>; } -export default function StepTwo({ - transferData, - setTransferData, - destination, - setDestination, - transferNotice, - setTransferNotice, -}: StepTwoProps) { - const { plots } = useFarmerBeanstalkRepayment().pods; - const [selectedPlots, setSelectedPlots] = useState([]); - const [amount, setAmount] = useState("0"); - const [range, setRange] = useState<[TokenValue, TokenValue]>([TokenValue.ZERO, TokenValue.ZERO]); - - useEffect(() => { - const _newPlots: Plot[] = []; - for (const data of transferData) { - const _plot = plots.find((plot) => plot.index.eq(data.id)); - if (_plot) { - _newPlots.push(_plot); - } - } - setSelectedPlots(_newPlots); - }, []); - - useEffect(() => { - if (selectedPlots.length === 1) { - const plot = selectedPlots[0]; - setRange([plot.index, plot.index.add(plot.pods)]); - } - }, [selectedPlots]); - - const handleRangeChange = useCallback( - (newRange: TokenValue[]) => { - if (selectedPlots.length === 0 || selectedPlots.length > 1) return; - const plot = selectedPlots[0]; - const newStart = newRange[0]; - const newEnd = newRange[1]; - - const relativeStart = newStart.sub(plot.index); - const relativeEnd = newEnd.sub(plot.index); - - const newData = [ - { - id: plot.index, - start: relativeStart, - end: relativeEnd, - }, - ]; - - const newAmount = newEnd.sub(newStart).toHuman(); - - const batchUpdate = () => { - setRange([newStart, newEnd]); - setTransferData(newData); - setAmount(newAmount); - }; - batchUpdate(); - }, - [selectedPlots, setTransferData], - ); - - const handleAmountChange = useCallback( - (value: string) => { - const newAmount = value; - - if (selectedPlots.length === 0) return; - - const plot = selectedPlots[0]; - const amountValue = TokenValue.fromHuman(newAmount || "0", PODS.decimals); - const newEnd = plot.index.add(amountValue); - - const newStart = plot.index; - const relativeStart = newStart.sub(plot.index); - const relativeEnd = newEnd.sub(plot.index); - - const newData = [ - { - id: plot.index, - start: relativeStart, - end: relativeEnd, - }, - ]; - - const batchUpdate = () => { - setAmount(newAmount); - if (selectedPlots.length === 1) { - setRange([newStart, newEnd]); - setTransferData(newData); - } - }; - batchUpdate(); - }, - [selectedPlots, setTransferData], - ); - +export default function StepTwo({ destination, setDestination, transferNotice, setTransferNotice }: StepTwoProps) { return (
-
- - 1} - altText={selectedPlots.length > 1 ? "Balance:" : "Plot Balance:"} - /> -
@@ -148,11 +32,8 @@ export default function StepTwo({ /> )} - {" "} +
- {selectedPlots.length === 1 && ( - - )}
); } diff --git a/src/state/useFarmerBeanstalkRepayment.ts b/src/state/useFarmerBeanstalkRepayment.ts index f11dc1be..7661281d 100644 --- a/src/state/useFarmerBeanstalkRepayment.ts +++ b/src/state/useFarmerBeanstalkRepayment.ts @@ -94,7 +94,7 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { // Query for Barn Payback (bsFERT) data from Barn_Payback contract // balanceOfFertilized and balanceOfUnfertilized require fertilizer IDs. - // We pass empty arrays initially; these will return 0 until IDs are populated. + // TODO: Fetch actual fertilizer IDs owned by the user from the contract const fertilizerIds: bigint[] = []; const fertilizerQuery = useReadContracts({ From d81b93d494233c3659a2d8f65dd8cdb056c12675 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Fri, 13 Feb 2026 20:09:07 +0300 Subject: [PATCH 11/22] feat: implement beanstalk obligation repayment and claim functionality --- src/constants/abiSnippets.ts | 63 +++++++++ src/constants/address.ts | 7 +- src/hooks/useAllFertilizerIds.ts | 86 ++++++++++++ src/pages/Beanstalk.tsx | 4 +- .../components/BeanstalkFertilizerSection.tsx | 2 +- .../components/BeanstalkObligationsCard.tsx | 116 ++++++++++++++++- .../components/BeanstalkPodsSection.tsx | 2 +- .../components/BeanstalkSiloSection.tsx | 2 +- src/state/useBeanstalkGlobalStats.ts | 11 +- src/state/useFarmerBeanstalkRepayment.ts | 123 +++++++++++++++--- 10 files changed, 391 insertions(+), 25 deletions(-) create mode 100644 src/hooks/useAllFertilizerIds.ts diff --git a/src/constants/abiSnippets.ts b/src/constants/abiSnippets.ts index 13083b5c..8c7d901e 100644 --- a/src/constants/abiSnippets.ts +++ b/src/constants/abiSnippets.ts @@ -408,6 +408,16 @@ const siloPayback = [ stateMutability: "view", type: "function", }, + { + inputs: [ + { internalType: "address", name: "recipient", type: "address" }, + { internalType: "enum LibTransfer.To", name: "toMode", type: "uint8" }, + ], + name: "claim", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, ] as const; const barnPayback = [ @@ -455,6 +465,57 @@ const barnPayback = [ stateMutability: "view", type: "function", }, + { + inputs: [ + { internalType: "uint256[]", name: "ids", type: "uint256[]" }, + { internalType: "enum LibTransfer.To", name: "mode", type: "uint8" }, + ], + name: "claimFertilized", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; + +const contractPayback = [ + { + inputs: [], + name: "totalDistributed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalReceived", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; + +const fertilizerLinkedList = [ + { + inputs: [], + name: "getFirst", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getLast", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "uint128", name: "id", type: "uint128" }], + name: "getNext", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, ] as const; export const abiSnippets = { @@ -486,4 +547,6 @@ export const abiSnippets = { erc721Enum, siloPayback, barnPayback, + contractPayback, + fertilizerLinkedList, } as const; diff --git a/src/constants/address.ts b/src/constants/address.ts index dd81e757..eeb70471 100644 --- a/src/constants/address.ts +++ b/src/constants/address.ts @@ -41,6 +41,7 @@ export const WELL_FUNCTION_ADDRESSES: ChainLookup<{ export const NFT_COLLECTION_1_CONTRACT: HashString = "0xBea5e200a087529AA3B62c460040aE94E3651b76"; -// TODO: Replace with actual deployed contract addresses -export const SILO_PAYBACK_ADDRESS: HashString = "0x0000000000000000000000000000000000000000"; // urBDV contract address -export const BARN_PAYBACK_ADDRESS: HashString = "0x0000000000000000000000000000000000000000"; // bsFERT contract address +// Deployed contract addresses for Beanstalk Shipments +export const SILO_PAYBACK_ADDRESS: HashString = "0x525C94754C51946a7a3B72580Ce0DF36922E1E64"; +export const BARN_PAYBACK_ADDRESS: HashString = "0x68bDbb0402a3Ca89C7C4af8e41C021635102d158"; +export const CONTRACT_PAYBACK_ADDRESS: HashString = "0xbA941Af3292c49b585f4EC5C8164c1dfc893EEdC"; diff --git a/src/hooks/useAllFertilizerIds.ts b/src/hooks/useAllFertilizerIds.ts new file mode 100644 index 00000000..ebc4761b --- /dev/null +++ b/src/hooks/useAllFertilizerIds.ts @@ -0,0 +1,86 @@ +import { abiSnippets } from "@/constants/abiSnippets"; +import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; +import { useCallback, useEffect, useState } from "react"; +import { usePublicClient } from "wagmi"; + +const MAX_ITERATIONS = 500; + +export interface UseAllFertilizerIdsReturn { + fertilizerIds: bigint[]; + isLoading: boolean; + isError: boolean; + refetch: () => Promise; +} + +/** + * Hook that traverses the Beanstalk Diamond contract's linked list + * to collect all global fertilizer IDs. + * + * Uses getFirst() to get the first ID, then getNext(id) to traverse + * until getNext returns 0 or MAX_ITERATIONS are reached. + * + * Does NOT require wallet connection โ€” this is global data. + */ +export function useAllFertilizerIds(): UseAllFertilizerIdsReturn { + const publicClient = usePublicClient(); + const protocolAddress = useProtocolAddress(); + + const [fertilizerIds, setFertilizerIds] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + + const fetchIds = useCallback(async () => { + if (!publicClient || !protocolAddress) { + setIsLoading(false); + return; + } + + try { + setIsLoading(true); + setIsError(false); + + const firstId = await publicClient.readContract({ + address: protocolAddress, + abi: abiSnippets.fertilizerLinkedList, + functionName: "getFirst", + }); + + if (!firstId || firstId === 0n) { + setFertilizerIds([]); + setIsLoading(false); + return; + } + + const ids: bigint[] = [BigInt(firstId)]; + let currentId = firstId; + + for (let i = 0; i < MAX_ITERATIONS; i++) { + const nextId = await publicClient.readContract({ + address: protocolAddress, + abi: abiSnippets.fertilizerLinkedList, + functionName: "getNext", + args: [currentId], + }); + + if (!nextId || nextId === 0n) break; + + ids.push(BigInt(nextId)); + currentId = nextId; + } + + setFertilizerIds(ids); + } catch (error) { + console.error("[useAllFertilizerIds] Linked list traverse failed:", error); + setFertilizerIds([]); + setIsError(true); + } finally { + setIsLoading(false); + } + }, [publicClient, protocolAddress]); + + useEffect(() => { + fetchIds(); + }, [fetchIds]); + + return { fertilizerIds, isLoading, isError, refetch: fetchIds }; +} diff --git a/src/pages/Beanstalk.tsx b/src/pages/Beanstalk.tsx index 7fe23357..74b91a74 100644 --- a/src/pages/Beanstalk.tsx +++ b/src/pages/Beanstalk.tsx @@ -17,8 +17,8 @@ const Beanstalk = () => {
Beanstalk Debt issued by Pinto. - Beanstalk participants at the time of Pinto launch were issued assets based on their holdings. When - Pinto exceeds 1 Billion in supply, 3% of mints go towards these between Beanstalk Silo Tokens, Pods, and + Beanstalk participants at the time of Pinto launch were issued assets based on their holdings. A portion + of new Pinto mints go towards repaying these obligations across Beanstalk Silo Tokens, Pods, and Fertilizer.{" "} = ({ disabled={disabled} actions={[ { label: "Rinse", onClick: onRinse, disabled: !hasBalance }, - { label: "Send", onClick: onSend }, + { label: "Send", onClick: onSend, disabled: !hasBalance }, ]} /> ); diff --git a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx index 2bc0ad65..db9fe51f 100644 --- a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx +++ b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx @@ -1,5 +1,12 @@ import { Button } from "@/components/ui/Button"; +import { abiSnippets } from "@/constants/abiSnippets"; +import { SILO_PAYBACK_ADDRESS } from "@/constants/address"; +import { beanstalkAbi, beanstalkAddress } from "@/generated/contractHooks"; +import useTransaction from "@/hooks/useTransaction"; import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; +import { useCallback } from "react"; +import { useNavigate } from "react-router-dom"; +import { encodeFunctionData } from "viem"; import { useAccount } from "wagmi"; import BeanstalkFertilizerSection from "./BeanstalkFertilizerSection"; import BeanstalkPodsSection from "./BeanstalkPodsSection"; @@ -12,11 +19,108 @@ import BeanstalkSiloSection from "./BeanstalkSiloSection"; */ const BeanstalkObligationsCard: React.FC = () => { const account = useAccount(); + const navigate = useNavigate(); const { silo, pods, fertilizer, isLoading, isError, refetch } = useFarmerBeanstalkRepayment(); const isConnected = !!account.address; const showDisabled = !isConnected || isError; + // Transaction hook for claim operations + const { writeWithEstimateGas, setSubmitting } = useTransaction({ + onSuccess: () => { + refetch(); + }, + successMessage: "Claim successful", + errorMessage: "Claim failed", + }); + + // Silo Claim โ€” Claim earned urBDV from SiloPayback + const handleClaimSilo = useCallback(async () => { + if (!account.address || silo.earned.isZero) return; + + try { + setSubmitting(true); + + // Encode claim(address recipient, enum LibTransfer.To toMode) call for SiloPayback contract + // toMode: 0 = INTERNAL (to internal balance), 1 = EXTERNAL (to wallet), 2 = INTERNAL_TOLERANT + const claimData = encodeFunctionData({ + abi: abiSnippets.siloPayback, + functionName: "claim", + args: [account.address, 1], // Claim to wallet (EXTERNAL) + }); + + await writeWithEstimateGas({ + address: beanstalkAddress[account.chainId ?? 8453], + abi: beanstalkAbi, + functionName: "farm", + args: [[claimData]], + }); + } catch (error) { + console.error("Silo claim error:", error); + setSubmitting(false); + } + }, [account.address, account.chainId, silo.earned, writeWithEstimateGas, setSubmitting]); + + // Pods Harvest โ€” Navigate to field harvest page + const handleHarvestPods = useCallback(() => { + // Navigate to field page with harvest action for fieldId=1 + navigate("/field?action=harvest&fieldId=1"); + }, [navigate]); + + // Fertilizer Rinse โ€” Rinse fertilized sprouts + const handleRinseFert = useCallback(async () => { + if (!account.address || fertilizer.fertilized.isZero) return; + + // Check if we have fertilizer IDs + if (!fertilizer.fertilizerIds || fertilizer.fertilizerIds.length === 0) { + console.warn( + "Cannot rinse fertilizer: No fertilizer IDs available. Fertilizer ID enumeration not yet implemented.", + ); + return; + } + + try { + setSubmitting(true); + + // Encode claimFertilized(uint256[] ids, enum LibTransfer.To mode) call for BarnPayback contract + // mode: 0 = INTERNAL (to internal balance), 1 = EXTERNAL (to wallet), 2 = INTERNAL_TOLERANT + const claimFertilizedData = encodeFunctionData({ + abi: abiSnippets.barnPayback, + functionName: "claimFertilized", + args: [fertilizer.fertilizerIds, 1], // Claim to wallet (EXTERNAL) + }); + + await writeWithEstimateGas({ + address: beanstalkAddress[account.chainId ?? 8453], + abi: beanstalkAbi, + functionName: "farm", + args: [[claimFertilizedData]], + }); + } catch (error) { + console.error("Fertilizer rinse error:", error); + setSubmitting(false); + } + }, [ + account.address, + account.chainId, + fertilizer.fertilized, + fertilizer.fertilizerIds, + writeWithEstimateGas, + setSubmitting, + ]); + + const handleSendSilo = () => { + navigate("/transfer/beanstalk-silo"); + }; + + const handleSendPods = () => { + navigate("/transfer/beanstalk-pods"); + }; + + const handleSendFertilizer = () => { + navigate("/transfer/beanstalk-fertilizer"); + }; + return (
{isConnected && isError && ( @@ -27,17 +131,27 @@ const BeanstalkObligationsCard: React.FC = () => {
)}
- +
diff --git a/src/pages/beanstalk/components/BeanstalkPodsSection.tsx b/src/pages/beanstalk/components/BeanstalkPodsSection.tsx index e26a2f5d..d721509f 100644 --- a/src/pages/beanstalk/components/BeanstalkPodsSection.tsx +++ b/src/pages/beanstalk/components/BeanstalkPodsSection.tsx @@ -36,7 +36,7 @@ const BeanstalkPodsSection: React.FC = ({ disabled={disabled} actions={[ { label: "Harvest", onClick: onHarvest, disabled: !hasPods }, - { label: "Send", onClick: onSend }, + { label: "Send", onClick: onSend, disabled: !hasPods }, ]} > {isLoading ? ( diff --git a/src/pages/beanstalk/components/BeanstalkSiloSection.tsx b/src/pages/beanstalk/components/BeanstalkSiloSection.tsx index c24ca4dd..07b041c6 100644 --- a/src/pages/beanstalk/components/BeanstalkSiloSection.tsx +++ b/src/pages/beanstalk/components/BeanstalkSiloSection.tsx @@ -30,7 +30,7 @@ const BeanstalkSiloSection: React.FC = ({ disabled={disabled} actions={[ { label: "Claim", onClick: onClaim, disabled: !hasBalance }, - { label: "Send", onClick: onSend }, + { label: "Send", onClick: onSend, disabled: !hasBalance }, ]} /> ); diff --git a/src/state/useBeanstalkGlobalStats.ts b/src/state/useBeanstalkGlobalStats.ts index 45db6383..922ea475 100644 --- a/src/state/useBeanstalkGlobalStats.ts +++ b/src/state/useBeanstalkGlobalStats.ts @@ -80,6 +80,12 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { abi: abiSnippets.barnPayback, functionName: "barnRemaining", }, + // Total Pinto received by Silo Payback contract + { + address: SILO_PAYBACK_ADDRESS, + abi: abiSnippets.siloPayback, + functionName: "totalReceived", + }, ], allowFailure: true, query: { @@ -104,12 +110,15 @@ export function useBeanstalkGlobalStats(): BeanstalkGlobalStatsData { const siloRemainingResult = globalQuery.data?.[1]?.result; const totalDistributedResult = globalQuery.data?.[2]?.result; const barnRemainingResult = globalQuery.data?.[3]?.result; + const totalReceivedResult = globalQuery.data?.[4]?.result; return { totalUrBdvDistributed: TokenValue.fromBlockchain(totalDistributedResult ?? 0n, URBDV.decimals), totalPodsInRepaymentField: TokenValue.fromBlockchain(totalPodsInRepaymentField ?? 0n, PODS.decimals), totalUnfertilizedSprouts: TokenValue.fromBlockchain(barnRemainingResult ?? 0n, SPROUTS.decimals), - totalPintoPaidOut: TokenValue.fromBlockchain(0n, URBDV.decimals), + // totalPintoPaidOut: Use totalReceived for now (will be > 0 once shipments start) + // Alternative: Could sum totalDistributed as a proxy for "issued" amount + totalPintoPaidOut: TokenValue.fromBlockchain(totalReceivedResult ?? 0n, URBDV.decimals), siloRemaining: TokenValue.fromBlockchain(siloRemainingResult ?? 0n, URBDV.decimals), barnRemaining: TokenValue.fromBlockchain(barnRemainingResult ?? 0n, SPROUTS.decimals), }; diff --git a/src/state/useFarmerBeanstalkRepayment.ts b/src/state/useFarmerBeanstalkRepayment.ts index 7661281d..82e9f0c3 100644 --- a/src/state/useFarmerBeanstalkRepayment.ts +++ b/src/state/useFarmerBeanstalkRepayment.ts @@ -5,6 +5,7 @@ import { BSFERT, PODS } from "@/constants/internalTokens"; import { defaultQuerySettings } from "@/constants/query"; import { beanstalkAbi } from "@/generated/contractHooks"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; +import { useAllFertilizerIds } from "@/hooks/useAllFertilizerIds"; import { Plot } from "@/utils/types"; import { useCallback, useMemo } from "react"; import { toHex } from "viem"; @@ -61,6 +62,12 @@ const URBDV_DECIMALS = 6; * - Pods data from repayment field (fieldId=1) - from on-chain * - Fertilizer data from Barn_Payback contract (fertilized, unfertilized balances) * + * Fertilizer flow (4 phases): + * 1. useAllFertilizerIds() โ€” get all global fertilizer IDs from linked list + * 2. multicall balanceOf(user, id) for each global ID + * 3. filter IDs where balance > 0 (userOwnedIds) + * 4. balanceOfFertilized + balanceOfUnfertilized for userOwnedIds + * * @returns FarmerBeanstalkRepaymentData with all farmer obligations data */ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { @@ -84,6 +91,20 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { functionName: "balanceOfPods", args: [farmerAddress, 1n], // fieldId=1 for repayment field }, + { + address: protocolAddress, + abi: [ + { + inputs: [{ name: "fieldId", type: "uint256" }], + name: "getHarvestableIndex", + outputs: [{ name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + ] as const, + functionName: "getHarvestableIndex", + args: [1n], // fieldId=1 for repayment field + }, ], allowFailure: true, query: { @@ -92,29 +113,65 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { }, }); - // Query for Barn Payback (bsFERT) data from Barn_Payback contract - // balanceOfFertilized and balanceOfUnfertilized require fertilizer IDs. - // TODO: Fetch actual fertilizer IDs owned by the user from the contract - const fertilizerIds: bigint[] = []; + // --- Phase 1: Get all global fertilizer IDs from linked list --- + const { + fertilizerIds: allFertilizerIds, + isLoading: fertIdsLoading, + isError: fertIdsError, + refetch: refetchFertIds, + } = useAllFertilizerIds(); + + // --- Phase 2: Multicall balanceOf(user, id) for each global ID --- + const balanceChecksEnabled = allFertilizerIds.length > 0 && farmerAddress !== ZERO_ADDRESS; + const balanceCheckContracts = useMemo( + () => + allFertilizerIds.map((id) => ({ + address: BARN_PAYBACK_ADDRESS, + abi: abiSnippets.barnPayback, + functionName: "balanceOf" as const, + args: [farmerAddress, id] as const, + })), + [allFertilizerIds, farmerAddress], + ); + const balanceChecks = useReadContracts({ + contracts: balanceChecksEnabled ? balanceCheckContracts : [], + allowFailure: true, + query: { + ...defaultQuerySettings, + enabled: balanceChecksEnabled, + }, + }); + + // --- Phase 3: Filter IDs where balance > 0 --- + const userOwnedIds = useMemo(() => { + if (!balanceChecks.data) return []; + return allFertilizerIds.filter((_, i) => { + const result = balanceChecks.data?.[i]; + return result?.status === "success" && (result.result as bigint) > 0n; + }); + }, [balanceChecks.data, allFertilizerIds]); + // --- Phase 4: balanceOfFertilized + balanceOfUnfertilized for userOwnedIds --- + const fertQueryEnabled = userOwnedIds.length > 0; const fertilizerQuery = useReadContracts({ contracts: [ { address: BARN_PAYBACK_ADDRESS, abi: abiSnippets.barnPayback, functionName: "balanceOfFertilized", - args: [farmerAddress, fertilizerIds], + args: [farmerAddress, userOwnedIds], }, { address: BARN_PAYBACK_ADDRESS, abi: abiSnippets.barnPayback, functionName: "balanceOfUnfertilized", - args: [farmerAddress, fertilizerIds], + args: [farmerAddress, userOwnedIds], }, ], allowFailure: true, query: { ...defaultQuerySettings, + enabled: fertQueryEnabled, }, }); @@ -185,10 +242,27 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { const plotsResult = podsQuery.data?.[0]?.result as readonly { index: bigint; pods: bigint }[] | undefined; const totalPodsResult = podsQuery.data?.[1]?.result; + const harvestableIndexResult = podsQuery.data?.[2]?.result as bigint | undefined; + + const harvestableIndex = TokenValue.fromBigInt(harvestableIndexResult ?? 0n, PODS.decimals); const plots: Plot[] = (plotsResult ?? []).map((plotData) => { const index = TokenValue.fromBigInt(plotData.index, PODS.decimals); const pods = TokenValue.fromBigInt(plotData.pods, PODS.decimals); + const endIndex = index.add(pods); + + let harvestablePods = TokenValue.ZERO; + let unharvestablePods = pods; + + if (harvestableIndex.gt(index)) { + if (harvestableIndex.gte(endIndex)) { + harvestablePods = pods; + unharvestablePods = TokenValue.ZERO; + } else { + harvestablePods = harvestableIndex.sub(index); + unharvestablePods = pods.sub(harvestablePods); + } + } return { id: index.toHuman(), @@ -196,8 +270,8 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { index, pods, harvestedPods: TokenValue.ZERO, - harvestablePods: TokenValue.ZERO, - unharvestablePods: pods, + harvestablePods, + unharvestablePods, }; }); @@ -214,7 +288,7 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { balance: TokenValue.ZERO, fertilized: TokenValue.ZERO, unfertilized: TokenValue.ZERO, - fertilizerIds, + fertilizerIds: userOwnedIds, }; } @@ -230,18 +304,37 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { balance, fertilized, unfertilized, - fertilizerIds, + fertilizerIds: userOwnedIds, }; - }, [fertilizerQuery.data, fertilizerQuery.isError, fertilizerIds]); + }, [fertilizerQuery.data, fertilizerQuery.isError, userOwnedIds]); // Refetch all queries const refetch = useCallback(async () => { - await Promise.all([siloQuery.refetch(), podsQuery.refetch(), fertilizerQuery.refetch()]); - }, [siloQuery.refetch, podsQuery.refetch, fertilizerQuery.refetch]); + await Promise.all([ + siloQuery.refetch(), + podsQuery.refetch(), + refetchFertIds(), + balanceChecks.refetch(), + fertilizerQuery.refetch(), + ]); + }, [siloQuery.refetch, podsQuery.refetch, refetchFertIds, balanceChecks.refetch, fertilizerQuery.refetch]); // Loading and error states from all queries - const isLoading = siloQuery.isLoading || podsQuery.isLoading || fertilizerQuery.isLoading; - const isError = siloQuery.isError || podsQuery.isError || fertilizerQuery.isError; + // Only count isError from queries that are actually enabled, + // disabled queries can report isError spuriously + const isLoading = + siloQuery.isLoading || + podsQuery.isLoading || + fertIdsLoading || + (balanceChecksEnabled && balanceChecks.isLoading) || + (fertQueryEnabled && fertilizerQuery.isLoading); + const isError = + siloQuery.isError || + podsQuery.isError || + fertIdsError || + (balanceChecksEnabled && balanceChecks.isError) || + (fertQueryEnabled && fertilizerQuery.isError); + (balanceChecksEnabled && balanceChecks.isError) || (fertQueryEnabled && fertilizerQuery.isError); return useMemo( () => ({ From 1f8ac96c994061b2167b76a001c3db2eec7ea179 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Fri, 13 Feb 2026 21:53:21 +0300 Subject: [PATCH 12/22] feat: enhance beanstalk repayment data fetching and transfer flows Refactor repayment logic to use event scanning for fertilizer IDs and direct contract interactions for urBDV and bsFERT transfers. This update also improves the UI with dynamic pod line grouping, detailed earned balance displays, and comprehensive batch transfer support. --- src/components/ComboInputField.tsx | 14 +- src/components/FertilizerCard.tsx | 5 +- src/components/PodLineGraph.tsx | 49 ++++- src/constants/abiSnippets.ts | 81 +++++--- src/hooks/useAllFertilizerIds.ts | 182 +++++++++++------- .../components/BeanstalkObligationsCard.tsx | 55 ++---- .../components/BeanstalkPodsSection.tsx | 12 +- .../components/BeanstalkSiloSection.tsx | 27 ++- src/pages/transfer/TransferActions.tsx | 24 ++- src/pages/transfer/actions/TransferAll.tsx | 116 +++++++++-- .../actions/TransferBeanstalkSilo.tsx | 43 ++--- src/pages/transfer/actions/all/FinalStep.tsx | 42 +++- .../actions/beanstalk-fertilizer/StepOne.tsx | 50 ++--- .../actions/beanstalk-pods/FinalStep.tsx | 4 +- .../actions/beanstalk-pods/StepOne.tsx | 8 +- .../actions/beanstalk-silo/FinalStep.tsx | 10 +- .../actions/beanstalk-silo/StepOne.tsx | 15 +- src/state/useFarmerBeanstalkRepayment.ts | 72 ++++++- 18 files changed, 544 insertions(+), 265 deletions(-) diff --git a/src/components/ComboInputField.tsx b/src/components/ComboInputField.tsx index dfdc9f27..75041aba 100644 --- a/src/components/ComboInputField.tsx +++ b/src/components/ComboInputField.tsx @@ -105,6 +105,7 @@ export interface ComboInputProps extends InputHTMLAttributes { // Additional info display showAdditionalInfo?: boolean; + hideUsdValue?: boolean; } function ComboInputField({ @@ -146,6 +147,7 @@ function ComboInputField({ enableSlider, sliderMarkers, showAdditionalInfo = true, + hideUsdValue = false, }: ComboInputProps) { const tokenData = useTokenData(); const { balances } = useFarmerBalances(); @@ -230,8 +232,9 @@ function ComboInputField({ } // If customMaxAmount is provided and greater than 0, use the minimum of base balance and customMaxAmount + // If base balance is 0 (no token selected or no farmer balance), use customMaxAmount directly if (customMaxAmount?.gt(0)) { - return TokenValue.min(baseBalance, customMaxAmount); + return baseBalance.gt(0) ? TokenValue.min(baseBalance, customMaxAmount) : customMaxAmount; } // Otherwise use base balance @@ -256,7 +259,12 @@ function ComboInputField({ return tokenAndBalanceMap.get(selectedToken) ?? TokenValue.ZERO; } // Always use farmerTokenBalance for display, not maxAmount (which may be limited by customMaxAmount) - return getFarmerBalanceByMode(farmerTokenBalance, balanceFrom); + const farmerBalance = getFarmerBalanceByMode(farmerTokenBalance, balanceFrom); + // If farmer balance is 0 and customMaxAmount is provided, show customMaxAmount as the balance + if (farmerBalance.eq(0) && customMaxAmount?.gt(0)) { + return customMaxAmount; + } + return farmerBalance; }, [mode, selectedPlots, tokenAndBalanceMap, selectedToken, farmerTokenBalance, balanceFrom, getFarmerBalanceByMode]); /** @@ -630,7 +638,7 @@ function ComboInputField({ {!disableInlineBalance && (
- {shouldShowAdditionalInfo() && mode !== "plots" ? ( + {shouldShowAdditionalInfo() && mode !== "plots" && !hideUsdValue ? ( {formatter.usd(inputValue)} diff --git a/src/components/FertilizerCard.tsx b/src/components/FertilizerCard.tsx index f1dc2117..59e11261 100644 --- a/src/components/FertilizerCard.tsx +++ b/src/components/FertilizerCard.tsx @@ -39,7 +39,10 @@ export default function FertilizerCard({ {/* Fertilizer info */}
-
{formatter.number(Number(fertId))} FERTILIZER
+
+ {formatter.number(Number(maxBalance))} bsFERT + ID {formatter.number(Number(fertId))} +
Sprouts: {sprouts} ยท Humidity: {humidity}
diff --git a/src/components/PodLineGraph.tsx b/src/components/PodLineGraph.tsx index 39d64a50..ccf5093a 100644 --- a/src/components/PodLineGraph.tsx +++ b/src/components/PodLineGraph.tsx @@ -53,14 +53,32 @@ interface PodLineGraphProps { label?: string; /** Optional: specify label type */ labelType?: "title" | "label"; + /** Optional: override harvestable index (for non-default fields like repayment field) */ + customHarvestableIndex?: TokenValue; + /** Optional: override pod index (for non-default fields like repayment field) */ + customPodIndex?: TokenValue; } /** * Groups nearby plots for visual display while keeping each plot individually interactive */ -function combinePlots(plots: Plot[], harvestableIndex: TokenValue, selectedIndices: Set): CombinedPlot[] { +function combinePlots( + plots: Plot[], + harvestableIndex: TokenValue, + selectedIndices: Set, + podLine: TokenValue, +): CombinedPlot[] { if (plots.length === 0) return []; + // Dynamically scale the grouping gap based on pod line size + const podLineNum = podLine.toNumber(); + let maxGap = MAX_GAP_TO_COMBINE; + if (podLineNum > 200_000_000) { + maxGap = TokenValue.fromHuman("50000000", PODS.decimals); // 50M gap for large pod lines + } else if (podLineNum > 50_000_000) { + maxGap = TokenValue.fromHuman("10000000", PODS.decimals); // 10M gap for medium pod lines + } + // Sort plots by index const sortedPlots = [...plots].sort((a, b) => a.index.sub(b.index).toNumber()); @@ -78,7 +96,7 @@ function combinePlots(plots: Plot[], harvestableIndex: TokenValue, selectedIndic const gap = nextPlot.index.sub(plot.index.add(plot.pods)); // If gap is small enough, continue grouping - if (gap.lt(MAX_GAP_TO_COMBINE)) { + if (gap.lt(maxGap)) { continue; } } @@ -116,9 +134,19 @@ function combinePlots(plots: Plot[], harvestableIndex: TokenValue, selectedIndic */ function generateAxisLabels(min: number, max: number): number[] { const labels: number[] = []; - const start = Math.floor(min / AXIS_INTERVAL) * AXIS_INTERVAL; + const range = max - min; + + // Dynamically choose interval based on range size + let interval = AXIS_INTERVAL; // default 10M + if (range > 200_000_000) { + interval = 100_000_000; // 100M intervals for large ranges + } else if (range > 50_000_000) { + interval = 50_000_000; // 50M intervals for medium ranges + } - for (let value = start; value <= max; value += AXIS_INTERVAL) { + const start = Math.floor(min / interval) * interval; + + for (let value = start; value <= max; value += interval) { if (value >= min) { labels.push(value); } @@ -191,10 +219,15 @@ export default function PodLineGraph({ className, label = "My Pods In Line", labelType = "label", + customHarvestableIndex, + customPodIndex, }: PodLineGraphProps) { const farmerField = useFarmerField(); - const harvestableIndex = useHarvestableIndex(); - const podIndex = usePodIndex(); + const defaultHarvestableIndex = useHarvestableIndex(); + const defaultPodIndex = usePodIndex(); + + const harvestableIndex = customHarvestableIndex ?? defaultHarvestableIndex; + const podIndex = customPodIndex ?? defaultPodIndex; const [hoveredPlotIndex, setHoveredPlotIndex] = useState(null); const [tooltipData, setTooltipData] = useState<{ @@ -253,8 +286,8 @@ export default function PodLineGraph({ // Combine plots for visualization const combinedPlots = useMemo( - () => combinePlots(plots, harvestableIndex, selectedSet), - [plots, harvestableIndex, selectedSet], + () => combinePlots(plots, harvestableIndex, selectedSet, podLine), + [plots, harvestableIndex, selectedSet, podLine], ); // Separate harvested and unharvested plots diff --git a/src/constants/abiSnippets.ts b/src/constants/abiSnippets.ts index 8c7d901e..a8cf1e99 100644 --- a/src/constants/abiSnippets.ts +++ b/src/constants/abiSnippets.ts @@ -418,6 +418,16 @@ const siloPayback = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + ], + name: "transfer", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function", + }, ] as const; const barnPayback = [ @@ -437,7 +447,7 @@ const barnPayback = [ { internalType: "uint256[]", name: "ids", type: "uint256[]" }, ], name: "balanceOfFertilized", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + outputs: [{ internalType: "uint256", name: "beans", type: "uint256" }], stateMutability: "view", type: "function", }, @@ -447,7 +457,7 @@ const barnPayback = [ { internalType: "uint256[]", name: "ids", type: "uint256[]" }, ], name: "balanceOfUnfertilized", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + outputs: [{ internalType: "uint256", name: "beans", type: "uint256" }], stateMutability: "view", type: "function", }, @@ -461,58 +471,70 @@ const barnPayback = [ { inputs: [], name: "fert", - outputs: [{ internalType: "address", name: "", type: "address" }], + outputs: [ + { internalType: "uint128", name: "fertilizedIndex", type: "uint128" }, + { internalType: "uint128", name: "unfertilizedIndex", type: "uint128" }, + { internalType: "uint128", name: "fertilizedPaidIndex", type: "uint128" }, + { internalType: "uint128", name: "leftoverBeans", type: "uint128" }, + { internalType: "uint128", name: "activeFertilizer", type: "uint128" }, + { internalType: "uint128", name: "fertFirst", type: "uint128" }, + { internalType: "uint128", name: "fertLast", type: "uint128" }, + { internalType: "uint128", name: "bpf", type: "uint128" }, + ], stateMutability: "view", type: "function", }, { inputs: [ - { internalType: "uint256[]", name: "ids", type: "uint256[]" }, - { internalType: "enum LibTransfer.To", name: "mode", type: "uint8" }, + { internalType: "address", name: "account", type: "address" }, + { internalType: "uint256", name: "id", type: "uint256" }, ], - name: "claimFertilized", - outputs: [], - stateMutability: "payable", + name: "lastBalanceOf", + outputs: [ + { + components: [ + { internalType: "uint128", name: "amount", type: "uint128" }, + { internalType: "uint128", name: "lastBpf", type: "uint128" }, + ], + internalType: "struct BeanstalkFertilizer.Balance", + name: "", + type: "tuple", + }, + ], + stateMutability: "view", type: "function", }, -] as const; - -const contractPayback = [ { - inputs: [], - name: "totalDistributed", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], - stateMutability: "view", + inputs: [ + { internalType: "uint256[]", name: "ids", type: "uint256[]" }, + { internalType: "uint8", name: "mode", type: "uint8" }, + ], + name: "claimFertilized", + outputs: [], + stateMutability: "nonpayable", type: "function", }, { inputs: [], - name: "totalReceived", - outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + name: "totalUnfertilizedBeans", + outputs: [{ internalType: "uint256", name: "beans", type: "uint256" }], stateMutability: "view", type: "function", }, ] as const; -const fertilizerLinkedList = [ +const contractPayback = [ { inputs: [], - name: "getFirst", - outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + name: "totalDistributed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], stateMutability: "view", type: "function", }, { inputs: [], - name: "getLast", - outputs: [{ internalType: "uint128", name: "", type: "uint128" }], - stateMutability: "view", - type: "function", - }, - { - inputs: [{ internalType: "uint128", name: "id", type: "uint128" }], - name: "getNext", - outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + name: "totalReceived", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], stateMutability: "view", type: "function", }, @@ -548,5 +570,4 @@ export const abiSnippets = { siloPayback, barnPayback, contractPayback, - fertilizerLinkedList, } as const; diff --git a/src/hooks/useAllFertilizerIds.ts b/src/hooks/useAllFertilizerIds.ts index ebc4761b..52e3a10a 100644 --- a/src/hooks/useAllFertilizerIds.ts +++ b/src/hooks/useAllFertilizerIds.ts @@ -1,86 +1,136 @@ import { abiSnippets } from "@/constants/abiSnippets"; -import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; -import { useCallback, useEffect, useState } from "react"; -import { usePublicClient } from "wagmi"; - -const MAX_ITERATIONS = 500; - -export interface UseAllFertilizerIdsReturn { - fertilizerIds: bigint[]; - isLoading: boolean; - isError: boolean; - refetch: () => Promise; -} +import { BARN_PAYBACK_ADDRESS } from "@/constants/address"; +import { useQuery } from "@tanstack/react-query"; +import { useCallback, useMemo } from "react"; +import { PublicClient, parseAbiItem } from "viem"; +import { usePublicClient, useReadContract } from "wagmi"; /** - * Hook that traverses the Beanstalk Diamond contract's linked list - * to collect all global fertilizer IDs. + * BarnPayback kontratฤฑndan tรผm aktif fertilizer ID'lerini รงeken hook. * - * Uses getFirst() to get the first ID, then getNext(id) to traverse - * until getNext returns 0 or MAX_ITERATIONS are reached. + * Strateji: + * 1. fert() รงaฤŸฤฑr โ†’ fertFirst, fertLast, activeFertilizer al + * 2. activeFertilizer == 0 && fertFirst == 0 ise โ†’ hiรง fertilizer yok, รงฤฑk + * 3. BarnPayback kontratฤฑnฤฑn TransferSingle/TransferBatch eventlerinden benzersiz ID'leri topla * - * Does NOT require wallet connection โ€” this is global data. + * NOT: Diamond'da getFirst()/getNext() yok. + * Linked list BarnPayback'in kendi iรงinde ama getNext() public getter olarak expose edilmemiลŸ. + * Bu yรผzden event scan kullanฤฑyoruz. */ -export function useAllFertilizerIds(): UseAllFertilizerIdsReturn { - const publicClient = usePublicClient(); - const protocolAddress = useProtocolAddress(); - const [fertilizerIds, setFertilizerIds] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [isError, setIsError] = useState(false); +const BARN_PAYBACK = BARN_PAYBACK_ADDRESS as `0x${string}`; - const fetchIds = useCallback(async () => { - if (!publicClient || !protocolAddress) { - setIsLoading(false); - return; - } +const transferSingleEvent = parseAbiItem( + "event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value)", +); - try { - setIsLoading(true); - setIsError(false); +const transferBatchEvent = parseAbiItem( + "event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values)", +); - const firstId = await publicClient.readContract({ - address: protocolAddress, - abi: abiSnippets.fertilizerLinkedList, - functionName: "getFirst", - }); +export interface FertData { + fertilizedIndex: bigint; + unfertilizedIndex: bigint; + fertilizedPaidIndex: bigint; + leftoverBeans: bigint; + activeFertilizer: bigint; + fertFirst: bigint; + fertLast: bigint; + bpf: bigint; +} - if (!firstId || firstId === 0n) { - setFertilizerIds([]); - setIsLoading(false); - return; - } +/** Fetch unique fertilizer IDs from event logs */ +async function fetchFertilizerIds(publicClient: PublicClient): Promise { + const uniqueIds = new Set(); - const ids: bigint[] = [BigInt(firstId)]; - let currentId = firstId; + // Scan TransferSingle events + const singleLogs = await publicClient.getLogs({ + address: BARN_PAYBACK, + event: transferSingleEvent, + fromBlock: "earliest", + toBlock: "latest", + }); - for (let i = 0; i < MAX_ITERATIONS; i++) { - const nextId = await publicClient.readContract({ - address: protocolAddress, - abi: abiSnippets.fertilizerLinkedList, - functionName: "getNext", - args: [currentId], - }); + for (const log of singleLogs) { + if (log.args.id !== undefined) { + uniqueIds.add(log.args.id); + } + } - if (!nextId || nextId === 0n) break; + // Scan TransferBatch events + const batchLogs = await publicClient.getLogs({ + address: BARN_PAYBACK, + event: transferBatchEvent, + fromBlock: "earliest", + toBlock: "latest", + }); - ids.push(BigInt(nextId)); - currentId = nextId; + for (const log of batchLogs) { + if (log.args.ids) { + for (const id of log.args.ids) { + uniqueIds.add(id); } - - setFertilizerIds(ids); - } catch (error) { - console.error("[useAllFertilizerIds] Linked list traverse failed:", error); - setFertilizerIds([]); - setIsError(true); - } finally { - setIsLoading(false); } - }, [publicClient, protocolAddress]); + } + + return Array.from(uniqueIds).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); +} + +export function useAllFertilizerIds() { + const publicClient = usePublicClient(); + + // Step 1: fert() call โ€” get contract state + const fertQuery = useReadContract({ + address: BARN_PAYBACK, + abi: abiSnippets.barnPayback, + functionName: "fert", + query: { + staleTime: 5 * 60 * 1000, + }, + }); + + // Parse fert() results + const fertData = useMemo((): FertData | null => { + if (!fertQuery.data) return null; + const result = fertQuery.data as readonly [bigint, bigint, bigint, bigint, bigint, bigint, bigint, bigint]; + return { + fertilizedIndex: result[0], + unfertilizedIndex: result[1], + fertilizedPaidIndex: result[2], + leftoverBeans: result[3], + activeFertilizer: result[4], + fertFirst: result[5], + fertLast: result[6], + bpf: result[7], + }; + }, [fertQuery.data]); + + const hasActiveFert = fertData ? !(fertData.activeFertilizer === 0n && fertData.fertFirst === 0n) : false; + + // Step 2: Scan event logs โ€” cached via React Query (staleTime: 1 hour) + const idsQuery = useQuery({ + queryKey: ["beanstalk", "fertilizerIds", "eventScan"], + queryFn: () => fetchFertilizerIds(publicClient as PublicClient), + enabled: !!publicClient && !!fertData && hasActiveFert, + staleTime: 60 * 60 * 1000, // 1 hour โ€” these IDs rarely change + gcTime: 60 * 60 * 1000, // keep in cache 1 hour after last use + refetchOnMount: false, + refetchOnWindowFocus: false, + }); + + const refetch = useCallback(async () => { + await fertQuery.refetch(); + await idsQuery.refetch(); + }, [fertQuery, idsQuery]); - useEffect(() => { - fetchIds(); - }, [fetchIds]); + // If no active fertilizer, return empty without error + const fertIds = !hasActiveFert ? [] : idsQuery.data ?? []; - return { fertilizerIds, isLoading, isError, refetch: fetchIds }; + return { + fertilizerIds: fertIds, + fertData, + isLoading: fertQuery.isLoading || (hasActiveFert && idsQuery.isLoading), + isError: fertQuery.isError || (hasActiveFert && idsQuery.isError), + refetch, + }; } diff --git a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx index db9fe51f..7821540b 100644 --- a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx +++ b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx @@ -1,12 +1,10 @@ import { Button } from "@/components/ui/Button"; import { abiSnippets } from "@/constants/abiSnippets"; -import { SILO_PAYBACK_ADDRESS } from "@/constants/address"; -import { beanstalkAbi, beanstalkAddress } from "@/generated/contractHooks"; +import { BARN_PAYBACK_ADDRESS, SILO_PAYBACK_ADDRESS } from "@/constants/address"; import useTransaction from "@/hooks/useTransaction"; import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useCallback } from "react"; import { useNavigate } from "react-router-dom"; -import { encodeFunctionData } from "viem"; import { useAccount } from "wagmi"; import BeanstalkFertilizerSection from "./BeanstalkFertilizerSection"; import BeanstalkPodsSection from "./BeanstalkPodsSection"; @@ -34,32 +32,26 @@ const BeanstalkObligationsCard: React.FC = () => { errorMessage: "Claim failed", }); - // Silo Claim โ€” Claim earned urBDV from SiloPayback + // Silo Claim โ€” Claim earned urBDV from SiloPayback contract directly const handleClaimSilo = useCallback(async () => { if (!account.address || silo.earned.isZero) return; try { setSubmitting(true); - // Encode claim(address recipient, enum LibTransfer.To toMode) call for SiloPayback contract - // toMode: 0 = INTERNAL (to internal balance), 1 = EXTERNAL (to wallet), 2 = INTERNAL_TOLERANT - const claimData = encodeFunctionData({ + // Call claim(address recipient, enum LibTransfer.To toMode) directly on SiloPayback contract + // toMode: 0 = INTERNAL, 1 = EXTERNAL (to wallet), 2 = INTERNAL_TOLERANT + await writeWithEstimateGas({ + address: SILO_PAYBACK_ADDRESS, abi: abiSnippets.siloPayback, functionName: "claim", args: [account.address, 1], // Claim to wallet (EXTERNAL) }); - - await writeWithEstimateGas({ - address: beanstalkAddress[account.chainId ?? 8453], - abi: beanstalkAbi, - functionName: "farm", - args: [[claimData]], - }); } catch (error) { console.error("Silo claim error:", error); setSubmitting(false); } - }, [account.address, account.chainId, silo.earned, writeWithEstimateGas, setSubmitting]); + }, [account.address, silo.earned, writeWithEstimateGas, setSubmitting]); // Pods Harvest โ€” Navigate to field harvest page const handleHarvestPods = useCallback(() => { @@ -67,47 +59,31 @@ const BeanstalkObligationsCard: React.FC = () => { navigate("/field?action=harvest&fieldId=1"); }, [navigate]); - // Fertilizer Rinse โ€” Rinse fertilized sprouts + // Fertilizer Rinse โ€” Rinse fertilized sprouts from BarnPayback contract directly const handleRinseFert = useCallback(async () => { if (!account.address || fertilizer.fertilized.isZero) return; - // Check if we have fertilizer IDs if (!fertilizer.fertilizerIds || fertilizer.fertilizerIds.length === 0) { - console.warn( - "Cannot rinse fertilizer: No fertilizer IDs available. Fertilizer ID enumeration not yet implemented.", - ); + console.warn("Cannot rinse fertilizer: No fertilizer IDs available."); return; } try { setSubmitting(true); - // Encode claimFertilized(uint256[] ids, enum LibTransfer.To mode) call for BarnPayback contract - // mode: 0 = INTERNAL (to internal balance), 1 = EXTERNAL (to wallet), 2 = INTERNAL_TOLERANT - const claimFertilizedData = encodeFunctionData({ + // Call claimFertilized(uint256[] ids, uint8 mode) directly on BarnPayback contract + // mode: 0 = INTERNAL, 1 = EXTERNAL (to wallet), 2 = INTERNAL_TOLERANT + await writeWithEstimateGas({ + address: BARN_PAYBACK_ADDRESS, abi: abiSnippets.barnPayback, functionName: "claimFertilized", args: [fertilizer.fertilizerIds, 1], // Claim to wallet (EXTERNAL) }); - - await writeWithEstimateGas({ - address: beanstalkAddress[account.chainId ?? 8453], - abi: beanstalkAbi, - functionName: "farm", - args: [[claimFertilizedData]], - }); } catch (error) { console.error("Fertilizer rinse error:", error); setSubmitting(false); } - }, [ - account.address, - account.chainId, - fertilizer.fertilized, - fertilizer.fertilizerIds, - writeWithEstimateGas, - setSubmitting, - ]); + }, [account.address, fertilizer.fertilized, fertilizer.fertilizerIds, writeWithEstimateGas, setSubmitting]); const handleSendSilo = () => { navigate("/transfer/beanstalk-silo"); @@ -133,6 +109,7 @@ const BeanstalkObligationsCard: React.FC = () => {
{ void; @@ -20,6 +22,8 @@ interface BeanstalkPodsSectionProps { const BeanstalkPodsSection: React.FC = ({ plots, totalPods, + harvestableIndex, + podIndex, isLoading, disabled = false, onHarvest, @@ -43,7 +47,13 @@ const BeanstalkPodsSection: React.FC = ({ ) : (
- +
)} diff --git a/src/pages/beanstalk/components/BeanstalkSiloSection.tsx b/src/pages/beanstalk/components/BeanstalkSiloSection.tsx index 07b041c6..ac6415bd 100644 --- a/src/pages/beanstalk/components/BeanstalkSiloSection.tsx +++ b/src/pages/beanstalk/components/BeanstalkSiloSection.tsx @@ -1,9 +1,11 @@ import { TokenValue } from "@/classes/TokenValue"; import BeanstalkStatField from "@/components/BeanstalkStatField"; +import TextSkeleton from "@/components/TextSkeleton"; import { formatter } from "@/utils/format"; interface BeanstalkSiloSectionProps { balance: TokenValue; + earned: TokenValue; isLoading: boolean; disabled?: boolean; onClaim?: () => void; @@ -12,27 +14,46 @@ interface BeanstalkSiloSectionProps { /** * Section component displaying urBDV token balance for Silo Payback + * Shows earned (claimable) as primary value, total urBDV balance as secondary */ const BeanstalkSiloSection: React.FC = ({ balance, + earned, isLoading, disabled = false, onClaim, onSend, }) => { const hasBalance = !balance.isZero; + const hasEarned = !earned.isZero; return ( + > + + {disabled ? ( + N/A + ) : ( +
+
+ {formatter.number(earned, { minDecimals: 2, maxDecimals: 2 })} + earned +
+
+ {formatter.number(balance, { minDecimals: 2, maxDecimals: 2 })} urBDV total +
+
+ )} +
+
); }; diff --git a/src/pages/transfer/TransferActions.tsx b/src/pages/transfer/TransferActions.tsx index 54ad0a3a..fea40e4d 100644 --- a/src/pages/transfer/TransferActions.tsx +++ b/src/pages/transfer/TransferActions.tsx @@ -8,6 +8,7 @@ import { useFarmerField } from "@/state/useFarmerField"; import { useFarmerSilo } from "@/state/useFarmerSilo"; import { usePriceData } from "@/state/usePriceData"; import { formatter } from "@/utils/format"; +import { useMemo } from "react"; import { Link } from "react-router-dom"; export default function TransferActions() { @@ -18,6 +19,15 @@ export default function TransferActions() { const farmerField = useFarmerField(); const repayment = useFarmerBeanstalkRepayment(); + // Compute total bsFERT token count from per-ID balances + const totalBsFert = useMemo(() => { + let total = 0n; + for (const detail of repayment.fertilizer.perIdData.values()) { + total += detail.balance; + } + return total; + }, [repayment.fertilizer.perIdData]); + const totalInternalBalance = Array.from(farmerBalance.balances).reduce( (total: TokenValue, tokenBalance) => total.add( @@ -27,7 +37,13 @@ export default function TransferActions() { TokenValue.ZERO, ); - const disableSendAll = totalInternalBalance.eq(0) && farmerSilo.depositsUSD.eq(0) && farmerField.totalPods.eq(0); + const disableSendAll = + totalInternalBalance.eq(0) && + farmerSilo.depositsUSD.eq(0) && + farmerField.totalPods.eq(0) && + repayment.silo.balance.eq(0) && + repayment.pods.totalPods.eq(0) && + totalBsFert === 0n; return (
@@ -84,17 +100,17 @@ export default function TransferActions() { > Beanstalk Repayment Silo Tokens - {formatter.usd(repayment.silo.balance)} + {`${formatter.twoDec(repayment.silo.balance)} urBDV`} @@ -54,7 +55,9 @@ const BeanstalkStatField: React.FC = ({ children ) : ( -
{disabled ? N/A : value}
+
+ {disabled ? N/A : value} +
)}
diff --git a/src/components/ReadMoreAccordion.tsx b/src/components/ReadMoreAccordion.tsx index 4a804605..b2b046cd 100644 --- a/src/components/ReadMoreAccordion.tsx +++ b/src/components/ReadMoreAccordion.tsx @@ -1,5 +1,5 @@ import { cn } from "@/utils/utils"; -import { motion } from "framer-motion"; +import { AnimatePresence, motion } from "framer-motion"; import { useState } from "react"; import { Col } from "./Container"; @@ -26,20 +26,21 @@ export default function ReadMoreAccordion({ if (inline) { return ( - + {open && ( + + {children} + )} - > - {open && {children}} - + {open ? " Read less" : " Read more"} diff --git a/src/pages/Beanstalk.tsx b/src/pages/Beanstalk.tsx index 74b91a74..597bcb82 100644 --- a/src/pages/Beanstalk.tsx +++ b/src/pages/Beanstalk.tsx @@ -16,7 +16,9 @@ const Beanstalk = () => {
Beanstalk Obligations
Beanstalk Debt issued by Pinto. - +
+ + Beanstalk participants at the time of Pinto launch were issued assets based on their holdings. A portion of new Pinto mints go towards repaying these obligations across Beanstalk Silo Tokens, Pods, and Fertilizer.{" "} @@ -28,13 +30,13 @@ const Beanstalk = () => { > Learn more - -
+ +
{/* Main Cards - Two Column Layout */} -
+
{/* Left Panel - Obligations Card */} diff --git a/src/pages/Market.tsx b/src/pages/Market.tsx index 7bfbe64d..b9cecdc4 100644 --- a/src/pages/Market.tsx +++ b/src/pages/Market.tsx @@ -998,15 +998,17 @@ export function Market() { Buy and sell Pods on the open market.
- - The Pod Market is a decentralized marketplace where users can trade Pods, which are protocol-native debt - instruments that represent future Pinto tokens. When you buy Pods, you're essentially purchasing the right - to redeem them for Pinto tokens at a fixed rate when they become harvestable. The market operates on a - first-in-first-out (FIFO) basis, meaning the oldest Pods become harvestable first. You can place buy - orders to acquire Pods at a specific price, or create listings to sell your existing Pods to other users. - The scatter chart above visualizes all active orders and listings, showing their place in line and price - per Pod. This allows you to see market depth and make informed trading decisions based on current market - conditions and your investment strategy. + + + The Pod Market is a decentralized marketplace where users can trade Pods, which are protocol-native debt + instruments that represent future Pinto tokens. When you buy Pods, you're essentially purchasing the + right to redeem them for Pinto tokens at a fixed rate when they become harvestable. The market operates + on a first-in-first-out (FIFO) basis, meaning the oldest Pods become harvestable first. You can place + buy orders to acquire Pods at a specific price, or create listings to sell your existing Pods to other + users. The scatter chart above visualizes all active orders and listings, showing their place in line + and price per Pod. This allows you to see market depth and make informed trading decisions based on + current market conditions and your investment strategy. + diff --git a/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx b/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx index 6acad9ef..74ccbd88 100644 --- a/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx +++ b/src/pages/beanstalk/components/BeanstalkFertilizerSection.tsx @@ -1,8 +1,13 @@ +import { TokenValue } from "@/classes/TokenValue"; import BeanstalkStatField from "@/components/BeanstalkStatField"; +import TextSkeleton from "@/components/TextSkeleton"; import { formatter } from "@/utils/format"; interface BeanstalkFertilizerSectionProps { tokenCount: bigint; + fertilized: TokenValue; + unfertilized: TokenValue; + totalUnfertilizedSprouts: TokenValue; isLoading: boolean; disabled?: boolean; onRinse?: () => void; @@ -11,27 +16,52 @@ interface BeanstalkFertilizerSectionProps { /** * Section component displaying fertilizer token count (bsFERT ERC1155 balance) + * Shows unfertilized sprouts as primary value, fertilized (rinsable) as secondary */ const BeanstalkFertilizerSection: React.FC = ({ tokenCount, + fertilized, + unfertilized, + totalUnfertilizedSprouts, isLoading, disabled = false, onRinse, onSend, }) => { const hasBalance = tokenCount > 0n; + const hasRinsable = !fertilized.isZero; + + const sharePercent = totalUnfertilizedSprouts.gt(0) + ? ((unfertilized.toNumber() / totalUnfertilizedSprouts.toNumber()) * 100).toFixed(2) + : "0.00"; return ( + > + + {disabled ? ( + N/A + ) : ( +
+
+ {formatter.number(unfertilized, { minDecimals: 2, maxDecimals: 2 })} + Sprouts ({sharePercent}%) +
+
+ {formatter.number(fertilized, { minDecimals: 2, maxDecimals: 2 })} Fertilized +
+
+ )} +
+
); }; diff --git a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx index cf6eb8d1..c596a240 100644 --- a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx +++ b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx @@ -3,6 +3,7 @@ import { abiSnippets } from "@/constants/abiSnippets"; import { BARN_PAYBACK_ADDRESS, SILO_PAYBACK_ADDRESS } from "@/constants/address"; import { beanstalkAbi, beanstalkAddress } from "@/generated/contractHooks"; import useTransaction from "@/hooks/useTransaction"; +import { useBeanstalkGlobalStats } from "@/state/useBeanstalkGlobalStats"; import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useCallback, useMemo } from "react"; import { useNavigate } from "react-router-dom"; @@ -21,6 +22,7 @@ const BeanstalkObligationsCard: React.FC = () => { const chainId = useChainId(); const navigate = useNavigate(); const { silo, pods, fertilizer, isLoading, isError, refetch } = useFarmerBeanstalkRepayment(); + const globalStats = useBeanstalkGlobalStats(); const isConnected = !!account.address; const showDisabled = !isConnected || isError; @@ -119,6 +121,10 @@ const BeanstalkObligationsCard: React.FC = () => { navigate("/transfer/beanstalk-pods"); }; + const handleMarketPods = () => { + navigate("/market/pods/buy/fill"); + }; + const handleSendFertilizer = () => { navigate("/transfer/beanstalk-fertilizer"); }; @@ -136,6 +142,7 @@ const BeanstalkObligationsCard: React.FC = () => { { disabled={showDisabled} onHarvest={handleHarvestPods} onSend={handleSendPods} + onMarket={handleMarketPods} /> void; onSend?: () => void; + onMarket?: () => void; } /** @@ -28,6 +29,7 @@ const BeanstalkPodsSection: React.FC = ({ disabled = false, onHarvest, onSend, + onMarket, }) => { const hasPlots = plots.length > 0; const hasPods = !totalPods.isZero; @@ -41,6 +43,7 @@ const BeanstalkPodsSection: React.FC = ({ disabled={disabled} actions={[ { label: "Harvest", onClick: onHarvest, disabled: !hasHarvestablePods }, + { label: "Buy/Sell", onClick: onMarket }, { label: "Send", onClick: onSend, disabled: !hasPods }, ]} > @@ -50,7 +53,7 @@ const BeanstalkPodsSection: React.FC = ({
void; @@ -14,11 +15,12 @@ interface BeanstalkSiloSectionProps { /** * Section component displaying urBDV token balance for Silo Payback - * Shows earned (claimable) as primary value, total urBDV balance as secondary + * Shows total balance as primary value, earned (claimable) as secondary */ const BeanstalkSiloSection: React.FC = ({ balance, earned, + totalDistributed, isLoading, disabled = false, onClaim, @@ -27,6 +29,10 @@ const BeanstalkSiloSection: React.FC = ({ const hasBalance = !balance.isZero; const hasEarned = !earned.isZero; + const sharePercent = totalDistributed.gt(0) + ? ((balance.toNumber() / totalDistributed.toNumber()) * 100).toFixed(2) + : "0.00"; + return ( = ({ > {disabled ? ( - N/A + N/A ) : (
-
- {formatter.number(earned, { minDecimals: 2, maxDecimals: 2 })} - earned +
+ {formatter.number(balance, { minDecimals: 2, maxDecimals: 2 })} + + Beanstalk Silo Tokens ({sharePercent}%) +
-
- {formatter.number(balance, { minDecimals: 2, maxDecimals: 2 })} urBDV total +
+ {formatter.number(earned, { minDecimals: 2, maxDecimals: 2 })} Earned
)} diff --git a/src/pages/transfer/actions/TransferBeanstalkPods.tsx b/src/pages/transfer/actions/TransferBeanstalkPods.tsx index e5af15a9..cbd934c5 100644 --- a/src/pages/transfer/actions/TransferBeanstalkPods.tsx +++ b/src/pages/transfer/actions/TransferBeanstalkPods.tsx @@ -10,7 +10,6 @@ import { type Address, encodeFunctionData } from "viem"; import { useAccount, useChainId } from "wagmi"; import FinalStep from "./beanstalk-pods/FinalStep"; import StepOne from "./beanstalk-pods/StepOne"; -import StepTwo from "./beanstalk-pods/StepTwo"; export interface PodTransferData { id: TokenValue; @@ -37,9 +36,7 @@ export default function TransferBeanstalkPods() { const stepDescription = () => { switch (step) { case 1: - return "Select Plots"; - case 2: - return "Enter address"; + return "Select Beanstalk Plots"; default: return "Confirm send"; } @@ -48,9 +45,7 @@ export default function TransferBeanstalkPods() { const enableNextStep = () => { switch (step) { case 1: - return transferData.length > 0; - case 2: - return !!destination && transferNotice; + return transferData.length > 0 && !!destination && transferNotice; default: return true; } @@ -107,16 +102,16 @@ export default function TransferBeanstalkPods() { {step === 1 ? ( - - ) : step === 2 ? ( - { switch (step) { case 1: - return "Select Plots"; - case 2: - return "Enter address"; + return "Select Pinto Plots"; default: return "Confirm send"; } @@ -48,9 +45,7 @@ export default function TransferPods() { const enableNextStep = () => { switch (step) { case 1: - return transferData.length > 0; - case 2: - return !!destination && transferNotice; + return transferData.length > 0 && !!destination && transferNotice; default: return true; } @@ -115,16 +110,16 @@ export default function TransferPods() { {step === 1 ? ( - - ) : step === 2 ? ( - >; + destination: string | undefined; + setDestination: Dispatch>; + transferNotice: boolean; + setTransferNotice: Dispatch>; } function sortPlotsByIndex(plots: Plot[]): Plot[] { return [...plots].sort((a, b) => a.index.sub(b.index).toNumber()); } -export default function StepOne({ transferData, setTransferData }: StepOneProps) { +export default function StepOne({ + transferData, + setTransferData, + destination, + setDestination, + transferNotice, + setTransferNotice, +}: StepOneProps) { const repaymentPods = useFarmerBeanstalkRepayment().pods; const { plots, harvestableIndex, podIndex } = repaymentPods; @@ -121,15 +136,16 @@ export default function StepOne({ transferData, setTransferData }: StepOneProps) ); return ( -
+
+ @@ -173,6 +189,32 @@ export default function StepOne({ transferData, setTransferData }: StepOneProps)
)} + + + + + + {destination && ( + + + + )} + +
); } diff --git a/src/pages/transfer/actions/pods/StepOne.tsx b/src/pages/transfer/actions/pods/StepOne.tsx index 9568a1ce..17041383 100644 --- a/src/pages/transfer/actions/pods/StepOne.tsx +++ b/src/pages/transfer/actions/pods/StepOne.tsx @@ -1,23 +1,38 @@ +import AddressInputField from "@/components/AddressInputField"; +import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; import PodLineGraph from "@/components/PodLineGraph"; +import { Label } from "@/components/ui/Label"; import { MultiSlider } from "@/components/ui/Slider"; import { useFarmerField } from "@/state/useFarmerField"; import { useHarvestableIndex } from "@/state/useFieldData"; import { formatter } from "@/utils/format"; import { computeTransferData, offsetToAbsoluteIndex } from "@/utils/podTransferUtils"; import { Plot } from "@/utils/types"; +import { AnimatePresence, motion } from "framer-motion"; import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { PodTransferData } from "../TransferPods"; interface StepOneProps { transferData: PodTransferData[]; setTransferData: Dispatch>; + destination: string | undefined; + setDestination: Dispatch>; + transferNotice: boolean; + setTransferNotice: Dispatch>; } function sortPlotsByIndex(plots: Plot[]): Plot[] { return [...plots].sort((a, b) => a.index.sub(b.index).toNumber()); } -export default function StepOne({ transferData, setTransferData }: StepOneProps) { +export default function StepOne({ + transferData, + setTransferData, + destination, + setDestination, + transferNotice, + setTransferNotice, +}: StepOneProps) { const { plots } = useFarmerField(); const harvestableIndex = useHarvestableIndex(); @@ -130,13 +145,14 @@ export default function StepOne({ transferData, setTransferData }: StepOneProps) ); return ( -
+
{/* Pod Line Graph Visualization */}
+ @@ -183,6 +199,32 @@ export default function StepOne({ transferData, setTransferData }: StepOneProps)
)} + + + + + + {destination && ( + + + + )} + +
); } diff --git a/src/state/useFarmerBeanstalkRepayment.ts b/src/state/useFarmerBeanstalkRepayment.ts index 9656c45f..9ef3ec35 100644 --- a/src/state/useFarmerBeanstalkRepayment.ts +++ b/src/state/useFarmerBeanstalkRepayment.ts @@ -3,6 +3,7 @@ import { abiSnippets } from "@/constants/abiSnippets"; import { BARN_PAYBACK_ADDRESS, SILO_PAYBACK_ADDRESS, ZERO_ADDRESS } from "@/constants/address"; import { BSFERT, PODS } from "@/constants/internalTokens"; import { defaultQuerySettings } from "@/constants/query"; +import { PINTO } from "@/constants/tokens"; import { beanstalkAbi } from "@/generated/contractHooks"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import { useAllFertilizerIds } from "@/hooks/useAllFertilizerIds"; @@ -351,8 +352,8 @@ export function useFarmerBeanstalkRepayment(): FarmerBeanstalkRepaymentData { const fertilizedResult = fertilizerQuery.data?.[0]?.result; const unfertilizedResult = fertilizerQuery.data?.[1]?.result; - const fertilized = TokenValue.fromBlockchain(fertilizedResult ?? 0n, BSFERT.decimals); - const unfertilized = TokenValue.fromBlockchain(unfertilizedResult ?? 0n, BSFERT.decimals); + const fertilized = TokenValue.fromBlockchain(fertilizedResult ?? 0n, PINTO.decimals); + const unfertilized = TokenValue.fromBlockchain(unfertilizedResult ?? 0n, PINTO.decimals); // Total balance is the sum of fertilized + unfertilized const balance = fertilized.add(unfertilized); From c4b1b6f15c8d0701a44bc56a1ac476ece7d6c986 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Mon, 16 Feb 2026 13:47:57 +0300 Subject: [PATCH 18/22] feat: integrate beanstalk marketplace toggle and field-specific logic --- src/context/BeanstalkMarketContext.tsx | 17 + src/encoders/createPodOrder.ts | 3 +- src/encoders/fillPodListing.ts | 3 +- src/generated/gql/pintostalk/gql.ts | 12 +- src/generated/gql/pintostalk/graphql.ts | 6 +- src/pages/Market.tsx | 404 ++++++++++-------- .../components/BeanstalkObligationsCard.tsx | 2 +- src/pages/market/MarketModeSelect.tsx | 22 +- src/pages/market/PodListingsTable.tsx | 4 +- src/pages/market/PodOrdersTable.tsx | 4 +- src/pages/market/actions/CancelListing.tsx | 6 +- src/pages/market/actions/CancelOrder.tsx | 6 +- src/pages/market/actions/CreateListing.tsx | 67 ++- src/pages/market/actions/CreateOrder.tsx | 14 +- src/pages/market/actions/FillListing.tsx | 33 +- src/pages/market/actions/FillOrder.tsx | 46 +- .../actions/beanstalk-pods/StepOne.tsx | 10 + .../podmarket/AllPodListings.graphql | 2 + .../beanstalk/podmarket/AllPodOrders.graphql | 3 +- src/state/market/usePodListings.ts | 13 +- src/state/market/usePodOrders.ts | 13 +- 21 files changed, 434 insertions(+), 256 deletions(-) create mode 100644 src/context/BeanstalkMarketContext.tsx diff --git a/src/context/BeanstalkMarketContext.tsx b/src/context/BeanstalkMarketContext.tsx new file mode 100644 index 00000000..c4fb08a5 --- /dev/null +++ b/src/context/BeanstalkMarketContext.tsx @@ -0,0 +1,17 @@ +import { createContext, useContext } from "react"; + +export interface BeanstalkMarketContextValue { + isBeanstalkMarketplace: boolean; + fieldId: bigint; + podMarketplaceId: string | undefined; +} + +export const BeanstalkMarketContext = createContext({ + isBeanstalkMarketplace: false, + fieldId: 0n, + podMarketplaceId: undefined, +}); + +export function useBeanstalkMarket() { + return useContext(BeanstalkMarketContext); +} diff --git a/src/encoders/createPodOrder.ts b/src/encoders/createPodOrder.ts index 15b52a97..ac1c4528 100644 --- a/src/encoders/createPodOrder.ts +++ b/src/encoders/createPodOrder.ts @@ -12,6 +12,7 @@ export default function createPodOrder( minFill?: TokenValue, balanceFrom?: FarmFromMode, clipboard?: `0x${string}`, + fieldId: bigint = 0n, ) { if (!amount || !pricePerPod || !maxPlaceInLine || !minFill || !balanceFrom) { return { @@ -26,7 +27,7 @@ export default function createPodOrder( args: [ { orderer: account, - fieldId: 0n, + fieldId: fieldId, pricePerPod, maxPlaceInLine: maxPlaceInLine.toBigInt(), minFillAmount: minFill.toBigInt(), diff --git a/src/encoders/fillPodListing.ts b/src/encoders/fillPodListing.ts index c5cb777a..59c075b4 100644 --- a/src/encoders/fillPodListing.ts +++ b/src/encoders/fillPodListing.ts @@ -16,6 +16,7 @@ export default function fillPodListing( amountIn?: TokenValue, // amountIn balanceFrom?: FarmFromMode, // fromMode clipboard?: `0x${string}`, + fieldId: bigint = 0n, // fieldId ) { if ( account === undefined || @@ -41,7 +42,7 @@ export default function fillPodListing( args: [ { lister: account, // account - fieldId: 0n, + fieldId: fieldId, index: index.toBigInt(), // index start: start.toBigInt(), // start podAmount: amount.toBigInt(), // amount diff --git a/src/generated/gql/pintostalk/gql.ts b/src/generated/gql/pintostalk/gql.ts index ec851937..f64db3c3 100644 --- a/src/generated/gql/pintostalk/gql.ts +++ b/src/generated/gql/pintostalk/gql.ts @@ -23,8 +23,8 @@ type Documents = { "query SiloSnapshots($first: Int!, $id: Bytes!) {\n siloHourlySnapshots(\n first: $first\n orderBy: season\n orderDirection: desc\n where: {silo_: {id: $id}}\n ) {\n beanToMaxLpGpPerBdvRatio\n deltaBeanMints\n season\n }\n}": typeof types.SiloSnapshotsDocument, "query SiloYields {\n siloYields(\n orderBy: season\n orderDirection: desc\n where: {emaWindow: ROLLING_30_DAY}\n first: 1\n ) {\n beansPerSeasonEMA\n beta\n createdAt\n season\n id\n u\n whitelistedTokens\n emaWindow\n tokenAPYS {\n beanAPY\n stalkAPY\n season\n createdAt\n token\n }\n }\n}": typeof types.SiloYieldsDocument, "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt, $listings_podMarketplace: String, $orders_podMarketplace: String) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL, podMarketplace: $listings_podMarketplace}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt, podMarketplace: $orders_podMarketplace}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}": typeof types.AllMarketActivityDocument, - "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}": typeof types.AllPodListingsDocument, - "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status}\n ) {\n ...PodOrder\n }\n}": typeof types.AllPodOrdersDocument, + "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0, $podMarketplace: String) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\", podMarketplace: $podMarketplace}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}": typeof types.AllPodListingsDocument, + "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0, $podMarketplace: String) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status, podMarketplace: $podMarketplace}\n ) {\n ...PodOrder\n }\n}": typeof types.AllPodOrdersDocument, "query FarmerMarketActivity($first: Int = 1000, $account: String!, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {farmer: $account, createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {farmer: $account, createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(\n first: $first\n where: {and: [{createdAt_gt: $fill_createdAt_gt}, {or: [{fromFarmer: $account}, {toFarmer: $account}]}]}\n ) {\n ...PodFill\n }\n}": typeof types.FarmerMarketActivityDocument, "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, @@ -51,8 +51,8 @@ const documents: Documents = { "query SiloSnapshots($first: Int!, $id: Bytes!) {\n siloHourlySnapshots(\n first: $first\n orderBy: season\n orderDirection: desc\n where: {silo_: {id: $id}}\n ) {\n beanToMaxLpGpPerBdvRatio\n deltaBeanMints\n season\n }\n}": types.SiloSnapshotsDocument, "query SiloYields {\n siloYields(\n orderBy: season\n orderDirection: desc\n where: {emaWindow: ROLLING_30_DAY}\n first: 1\n ) {\n beansPerSeasonEMA\n beta\n createdAt\n season\n id\n u\n whitelistedTokens\n emaWindow\n tokenAPYS {\n beanAPY\n stalkAPY\n season\n createdAt\n token\n }\n }\n}": types.SiloYieldsDocument, "query AllMarketActivity($first: Int = 1000, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt, $listings_podMarketplace: String, $orders_podMarketplace: String) {\n podListings(\n first: $first\n where: {createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL, podMarketplace: $listings_podMarketplace}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {createdAt_gt: $orders_createdAt_gt, podMarketplace: $orders_podMarketplace}\n ) {\n ...PodOrder\n }\n podFills(first: $first, where: {createdAt_gt: $fill_createdAt_gt}) {\n ...PodFill\n }\n}": types.AllMarketActivityDocument, - "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}": types.AllPodListingsDocument, - "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status}\n ) {\n ...PodOrder\n }\n}": types.AllPodOrdersDocument, + "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0, $podMarketplace: String) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\", podMarketplace: $podMarketplace}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}": types.AllPodListingsDocument, + "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0, $podMarketplace: String) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status, podMarketplace: $podMarketplace}\n ) {\n ...PodOrder\n }\n}": types.AllPodOrdersDocument, "query FarmerMarketActivity($first: Int = 1000, $account: String!, $listings_createdAt_gt: BigInt, $orders_createdAt_gt: BigInt, $fill_createdAt_gt: BigInt) {\n podListings(\n first: $first\n where: {farmer: $account, createdAt_gt: $listings_createdAt_gt, status_not: FILLED_PARTIAL}\n ) {\n ...PodListing\n }\n podOrders(\n first: $first\n orderBy: createdAt\n orderDirection: desc\n where: {farmer: $account, createdAt_gt: $orders_createdAt_gt}\n ) {\n ...PodOrder\n }\n podFills(\n first: $first\n where: {and: [{createdAt_gt: $fill_createdAt_gt}, {or: [{fromFarmer: $account}, {toFarmer: $account}]}]}\n ) {\n ...PodFill\n }\n}": types.FarmerMarketActivityDocument, "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, @@ -123,11 +123,11 @@ export function graphql(source: "query AllMarketActivity($first: Int = 1000, $li /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}"): (typeof documents)["query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}"]; +export function graphql(source: "query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0, $podMarketplace: String) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\", podMarketplace: $podMarketplace}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\n }\n}"): (typeof documents)["query AllPodListings($first: Int = 1000, $status: MarketStatus = ACTIVE, $maxHarvestableIndex: BigInt!, $skip: Int = 0, $podMarketplace: String) {\n podListings(\n first: $first\n skip: $skip\n where: {status: $status, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\", podMarketplace: $podMarketplace}\n orderBy: index\n orderDirection: asc\n ) {\n ...PodListing\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 AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status}\n ) {\n ...PodOrder\n }\n}"): (typeof documents)["query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status}\n ) {\n ...PodOrder\n }\n}"]; +export function graphql(source: "query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0, $podMarketplace: String) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status, podMarketplace: $podMarketplace}\n ) {\n ...PodOrder\n }\n}"): (typeof documents)["query AllPodOrders($first: Int = 1000, $status: MarketStatus = ACTIVE, $skip: Int = 0, $podMarketplace: String) {\n podOrders(\n first: $first\n skip: $skip\n orderBy: createdAt\n orderDirection: desc\n where: {status: $status, podMarketplace: $podMarketplace}\n ) {\n ...PodOrder\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 98e8681f..1ff4fd75 100644 --- a/src/generated/gql/pintostalk/graphql.ts +++ b/src/generated/gql/pintostalk/graphql.ts @@ -14119,6 +14119,7 @@ export type AllPodListingsQueryVariables = Exact<{ status?: InputMaybe; maxHarvestableIndex: Scalars['BigInt']['input']; skip?: InputMaybe; + podMarketplace?: InputMaybe; }>; @@ -14128,6 +14129,7 @@ export type AllPodOrdersQueryVariables = Exact<{ first?: InputMaybe; status?: InputMaybe; skip?: InputMaybe; + podMarketplace?: InputMaybe; }>; @@ -14254,8 +14256,8 @@ export const BeanstalkSeasonsTableDocument = {"kind":"Document","definitions":[{ export const SiloSnapshotsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SiloSnapshots"},"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":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Bytes"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloHourlySnapshots"},"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":"season"}},{"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":"silo_"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"beanToMaxLpGpPerBdvRatio"}},{"kind":"Field","name":{"kind":"Name","value":"deltaBeanMints"}},{"kind":"Field","name":{"kind":"Name","value":"season"}}]}}]}}]} as unknown as DocumentNode; export const SiloYieldsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SiloYields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloYields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"season"}},{"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":"emaWindow"},"value":{"kind":"EnumValue","value":"ROLLING_30_DAY"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"beansPerSeasonEMA"}},{"kind":"Field","name":{"kind":"Name","value":"beta"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"u"}},{"kind":"Field","name":{"kind":"Name","value":"whitelistedTokens"}},{"kind":"Field","name":{"kind":"Name","value":"emaWindow"}},{"kind":"Field","name":{"kind":"Name","value":"tokenAPYS"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"beanAPY"}},{"kind":"Field","name":{"kind":"Name","value":"stalkAPY"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"token"}}]}}]}}]}}]} as unknown as DocumentNode; export const AllMarketActivityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllMarketActivity"},"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":"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"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"listings_podMarketplace"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orders_podMarketplace"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"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":"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"}},{"kind":"ObjectField","name":{"kind":"Name","value":"podMarketplace"},"value":{"kind":"Variable","name":{"kind":"Name","value":"listings_podMarketplace"}}}]}}],"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":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orders_createdAt_gt"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"podMarketplace"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orders_podMarketplace"}}}]}}],"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":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}}}]}}],"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 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 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"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"podMarketplace"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"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":"ObjectField","name":{"kind":"Name","value":"podMarketplace"},"value":{"kind":"Variable","name":{"kind":"Name","value":"podMarketplace"}}}]}},{"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"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"podMarketplace"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"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"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"podMarketplace"},"value":{"kind":"Variable","name":{"kind":"Name","value":"podMarketplace"}}}]}}],"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":"totalReferralRewardPodsReceived_gt"},"value":{"kind":"StringValue","value":"0","block":false}}]}},{"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; diff --git a/src/pages/Market.tsx b/src/pages/Market.tsx index b9cecdc4..ec2a9c29 100644 --- a/src/pages/Market.tsx +++ b/src/pages/Market.tsx @@ -12,8 +12,10 @@ import { Card } from "@/components/ui/Card"; import { Separator } from "@/components/ui/Separator"; import { Switch } from "@/components/ui/Switch"; import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; +import { BeanstalkMarketContext } from "@/context/BeanstalkMarketContext"; import useNavHeight from "@/hooks/display/useNavHeight"; import { useAllMarket } from "@/state/market/useAllMarket"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useHarvestableIndex, usePodLine } from "@/state/useFieldData"; import { trackSimpleEvent } from "@/utils/analytics"; import { calculatePodScore } from "@/utils/podScore"; @@ -22,7 +24,7 @@ import { exists } from "@/utils/utils"; import { ActiveElement, ChartEvent, PointStyle, TooltipOptions } from "chart.js"; import { Chart } from "chart.js"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { AllActivityTable } from "./market/AllActivityTable"; import { FarmerActivityTable } from "./market/FarmerActivityTable"; import MarketModeSelect from "./market/MarketModeSelect"; @@ -214,14 +216,50 @@ export function Market() { const hoverInfoRef = useRef(null); const lastPositionSideRef = useRef<{ isRight: boolean; isAbove: boolean } | null>(null); const navigate = useNavigate(); - const [isBeanstalkMarketplace, setIsBeanstalkMarketplace] = useState(false); + const [searchParams, setSearchParams] = useSearchParams(); + const isBeanstalkMarketplace = searchParams.get("beanstalk") === "true"; + const fieldId = isBeanstalkMarketplace ? 1n : 0n; const podMarketplaceId = isBeanstalkMarketplace ? "1" : undefined; + + const handleToggleBeanstalk = useCallback( + (checked: boolean) => { + setSearchParams( + (prev) => { + const next = new URLSearchParams(prev); + if (checked) { + next.set("beanstalk", "true"); + } else { + next.delete("beanstalk"); + } + return next; + }, + { replace: true }, + ); + }, + [setSearchParams], + ); + const buildMarketPath = useCallback( + (path: string) => { + if (isBeanstalkMarketplace) { + return `${path}?beanstalk=true`; + } + return path; + }, + [isBeanstalkMarketplace], + ); + const { data, isLoaded } = useAllMarket(podMarketplaceId); const podLine = usePodLine(); - const podLineAsNumber = podLine.toNumber() / MILLION; + const harvestableIndex = useHarvestableIndex(); + const repayment = useFarmerBeanstalkRepayment(); + + // Toggle durumuna gรถre pod line ve harvestable index seรงimi + const activePodLine = isBeanstalkMarketplace ? repayment.pods.podIndex.sub(repayment.pods.harvestableIndex) : podLine; + const activeHarvestableIndex = isBeanstalkMarketplace ? repayment.pods.harvestableIndex : harvestableIndex; + + const podLineAsNumber = activePodLine.toNumber() / MILLION; // Chart rounds X max to nearest 10 (not ceil), so we need to match that for validation const chartXMax = Math.round((podLineAsNumber / 10) * 10); - const harvestableIndex = useHarvestableIndex(); const navHeight = useNavHeight(); const [mounted, setMounted] = useState(false); @@ -260,7 +298,7 @@ export function Market() { if (!selectedPlotData) return []; return selectedPlotData.listingData.map((data) => { - const placeInLine = data.index.sub(harvestableIndex).toNumber(); + const placeInLine = data.index.sub(activeHarvestableIndex).toNumber(); const placeInLineMillions = placeInLine / MILLION; const podScore = calculatePodScore(selectedPlotData.pricePerPod, placeInLineMillions); @@ -276,11 +314,11 @@ export function Market() { }; }); }, - [harvestableIndex], + [activeHarvestableIndex], ); const scatterChartData: MarketScatterChartData[] = useMemo(() => { - const baseData = shapeScatterChartData(data || [], harvestableIndex); + const baseData = shapeScatterChartData(data || [], activeHarvestableIndex); // Add selected plots dataset if available if (!selectedPlotData || selectedPlotData.listingData.length === 0 || selectedPlotData.pricePerPod <= 0) { @@ -320,7 +358,7 @@ export function Market() { }; return [...baseData, selectedPlotsDataset]; - }, [data, harvestableIndex, selectedPlotData, transformSelectedPlotsToChartPoints]); + }, [data, activeHarvestableIndex, selectedPlotData, transformSelectedPlotsToChartPoints]); // Calculate highlighted event IDs (filtered by FillListing parameters) const highlightedEventIds = useMemo(() => { @@ -522,13 +560,13 @@ export function Market() { useEffect(() => { if (!mode) { // No mode specified (e.g. /market/pods), redirect to buy/fill - navigate("/market/pods/buy/fill", { replace: true }); + navigate(buildMarketPath("/market/pods/buy/fill"), { replace: true }); } else if (mode === "buy" && !id) { - navigate("/market/pods/buy/fill", { replace: true }); + navigate(buildMarketPath("/market/pods/buy/fill"), { replace: true }); } else if (mode === "sell" && !id) { - navigate("/market/pods/sell/create", { replace: true }); + navigate(buildMarketPath("/market/pods/sell/create"), { replace: true }); } - }, [id, mode, navigate]); + }, [id, mode, navigate, buildMarketPath]); // Clear preview plots and unfreeze chart when route changes away from create listing // Also unfreeze when switching between listing/selling pages @@ -602,14 +640,14 @@ export function Market() { if (selection === TABLE_SLUGS[1]) { setIsNavigating(true); - navigate(`/market/pods/buy/fill`); + navigate(buildMarketPath(`/market/pods/buy/fill`)); } else if (selection === TABLE_SLUGS[2]) { setIsNavigating(true); - navigate(`/market/pods/sell/fill`); + navigate(buildMarketPath(`/market/pods/sell/fill`)); } handleChangeTab(selection); }, - [navigate, tab], + [navigate, tab, buildMarketPath], ); const handleSecondaryTabClick = useCallback( @@ -782,7 +820,7 @@ export function Market() { setIsCrosshairFrozen(false); setContextMenu(null); setIsContextMenuClosing(false); - navigate(path, { state }); + navigate(buildMarketPath(path), { state }); // Reset flag after navigation setTimeout(() => { @@ -790,7 +828,7 @@ export function Market() { }, 100); }, 200); // Match fade-out animation duration }, - [navigate], + [navigate, buildMarketPath], ); const contextMenuOptions = useMemo(() => { @@ -863,7 +901,7 @@ export function Market() { // Switch to buy mode and fill action if (mode !== "buy" || id !== "fill") { setIsNavigating(true); - navigate("/market/pods/buy/fill"); + navigate(buildMarketPath("/market/pods/buy/fill")); } handleChangeTab(TABLE_SLUGS[1]); // Switch to listings tab } else { @@ -874,7 +912,7 @@ export function Market() { // Switch to sell mode and fill action if (mode !== "sell" || id !== "fill") { setIsNavigating(true); - navigate("/market/pods/sell/fill"); + navigate(buildMarketPath("/market/pods/sell/fill")); } handleChangeTab(TABLE_SLUGS[2]); // Switch to orders tab } @@ -908,7 +946,7 @@ export function Market() { // Switch to buy mode and fill action if (mode !== "buy" || id !== "fill") { setIsNavigating(true); - navigate("/market/pods/buy/fill"); + navigate(buildMarketPath("/market/pods/buy/fill")); } handleChangeTab(TABLE_SLUGS[1]); // Switch to listings tab } else { @@ -919,7 +957,7 @@ export function Market() { // Switch to sell mode and fill action if (mode !== "sell" || id !== "fill") { setIsNavigating(true); - navigate("/market/pods/sell/fill"); + navigate(buildMarketPath("/market/pods/sell/fill")); } handleChangeTab(TABLE_SLUGS[2]); // Switch to orders tab } @@ -970,172 +1008,200 @@ export function Market() { }); // Navigate to CreateListing with plot indices (not full Plot objects to avoid serialization issues) - navigate("/market/pods/sell/create", { + navigate(buildMarketPath("/market/pods/sell/create"), { state: { selectedPlotIndices: plotIndices }, }); }, - [navigate], + [navigate, buildMarketPath], ); // Default to buy/fill when no mode is selected const viewMode = mode || "buy"; const viewAction = id || (viewMode === "buy" ? "fill" : "create"); + const contextValue = useMemo( + () => ({ + isBeanstalkMarketplace, + fieldId, + podMarketplaceId, + }), + [isBeanstalkMarketplace, fieldId, podMarketplaceId], + ); + return ( - <> -
-

Your screen size is too small to access the Pod Market.

-

- If you're on Desktop, zoom out on your browser to access the Pod Market. -

-
-
-
- -
-
Market
-
- Buy and sell Pods on the open market. -
-
- - - The Pod Market is a decentralized marketplace where users can trade Pods, which are protocol-native debt - instruments that represent future Pinto tokens. When you buy Pods, you're essentially purchasing the - right to redeem them for Pinto tokens at a fixed rate when they become harvestable. The market operates - on a first-in-first-out (FIFO) basis, meaning the oldest Pods become harvestable first. You can place - buy orders to acquire Pods at a specific price, or create listings to sell your existing Pods to other - users. The scatter chart above visualizes all active orders and listings, showing their place in line - and price per Pod. This allows you to see market depth and make informed trading decisions based on - current market conditions and your investment strategy. - - - - -
-
-
- Toggle Beanstalk Marketplace - + + <> +
+

Your screen size is too small to access the Pod Market.

+

+ If you're on Desktop, zoom out on your browser to access the Pod Market. +

+
+
+
+ +
+
Market
+
+ Buy and sell Pods on the open market. +
-
- {!isLoaded && ( -
- + + + The Pod Market is a decentralized marketplace where users can trade Pods, which are protocol-native + debt instruments that represent future Pinto tokens. When you buy Pods, you're essentially purchasing + the right to redeem them for Pinto tokens at a fixed rate when they become harvestable. The market + operates on a first-in-first-out (FIFO) basis, meaning the oldest Pods become harvestable first. You + can place buy orders to acquire Pods at a specific price, or create listings to sell your existing + Pods to other users. The scatter chart above visualizes all active orders and listings, showing their + place in line and price per Pod. This allows you to see market depth and make informed trading + decisions based on current market conditions and your investment strategy. + + + + +
+
+
+ Toggle Beanstalk Marketplace + +
+
+ {!isLoaded && ( +
+ +
+ )} + + + {/* Gradient Legend - positioned in top-right corner */} +
+
- )} - - - {/* Gradient Legend - positioned in top-right corner */} -
- +
+
+ +
+
+ {TABLE_SLUGS.map((s, idx) => ( +

+ {TABLE_LABELS[idx]} +

+ ))} +
+ +
+ {tab === TABLE_SLUGS[0] && } + {tab === TABLE_SLUGS[1] && } + {tab === TABLE_SLUGS[2] && } + {tab === TABLE_SLUGS[3] && }
-
- -
-
- {TABLE_SLUGS.map((s, idx) => ( -

- {TABLE_LABELS[idx]} -

- ))} -
- -
- {tab === TABLE_SLUGS[0] && } - {tab === TABLE_SLUGS[1] && } - {tab === TABLE_SLUGS[2] && } - {tab === TABLE_SLUGS[3] && } -
-
-
- -
- -
- {viewMode === "buy" && viewAction === "create" && } - {viewMode === "buy" && viewAction === "fill" && ( - - )} - {viewMode === "sell" && viewAction === "create" && ( - - )} - {viewMode === "sell" && viewAction === "fill" && ( - - )} +
+ +
+ +
+ {viewMode === "buy" && viewAction === "create" && ( + + )} + {viewMode === "buy" && viewAction === "fill" && ( + + )} + {viewMode === "sell" && viewAction === "create" && ( + + )} + {viewMode === "sell" && viewAction === "fill" && ( + + )} +
-
- + +
-
- - {/* Hover info - rendered once, updated via direct DOM manipulation for performance */} -
-
- Price per Pod: 0.000 -
-
- Place in line: 0.0M + + {/* Hover info - rendered once, updated via direct DOM manipulation for performance */} +
+
+ Price per Pod: 0.000 +
+
+ Place in line: 0.0M +
-
- - {contextMenu && ( - { - // Only unfreeze if NOT navigating (closed via Escape/click outside/scroll) - // If navigating, handleUnfreezeAndNavigate already handles unfreeze - if (!isNavigatingRef.current) { - // Trigger closing animation - setIsContextMenuClosing(true); - - // Wait for animation to complete before unfreezing - setTimeout(() => { - if (isCrosshairFrozen) { - chartRef.current?.unfreeze(); - setIsCrosshairFrozen(false); - } - setContextMenu(null); - setIsContextMenuClosing(false); - }, 200); // Match fade-out animation duration - } - }} - /> - )} - + + {contextMenu && ( + { + // Only unfreeze if NOT navigating (closed via Escape/click outside/scroll) + // If navigating, handleUnfreezeAndNavigate already handles unfreeze + if (!isNavigatingRef.current) { + // Trigger closing animation + setIsContextMenuClosing(true); + + // Wait for animation to complete before unfreezing + setTimeout(() => { + if (isCrosshairFrozen) { + chartRef.current?.unfreeze(); + setIsCrosshairFrozen(false); + } + setContextMenu(null); + setIsContextMenuClosing(false); + }, 200); // Match fade-out animation duration + } + }} + /> + )} + + ); } diff --git a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx index c596a240..112b8e82 100644 --- a/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx +++ b/src/pages/beanstalk/components/BeanstalkObligationsCard.tsx @@ -122,7 +122,7 @@ const BeanstalkObligationsCard: React.FC = () => { }; const handleMarketPods = () => { - navigate("/market/pods/buy/fill"); + navigate("/market/pods/buy/fill?beanstalk=true"); }; const handleSendFertilizer = () => { diff --git a/src/pages/market/MarketModeSelect.tsx b/src/pages/market/MarketModeSelect.tsx index fd03cd50..3e255630 100644 --- a/src/pages/market/MarketModeSelect.tsx +++ b/src/pages/market/MarketModeSelect.tsx @@ -3,7 +3,7 @@ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/Tabs"; import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import { trackSimpleEvent } from "@/utils/analytics"; import { useCallback, useMemo } from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; type MarketMode = "buy" | "sell"; type MarketAction = "create" | "fill"; @@ -34,6 +34,16 @@ const ACTION_LABELS: Record> = { export default function MarketModeSelect({ onMainSelectionChange, onSecondarySelectionChange }: MarketModeSelectProps) { const { mode, id } = useParams(); const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + // Helper to preserve current search params (e.g. ?beanstalk=true) during navigation + const buildPath = useCallback( + (path: string) => { + const params = searchParams.toString(); + return params ? `${path}?${params}` : path; + }, + [searchParams], + ); // Derive current state from URL params const { mainTab, secondaryTab, secondaryTabValue } = useMemo(() => { @@ -62,10 +72,10 @@ export default function MarketModeSelect({ onMainSelectionChange, onSecondarySel secondary_tab: secondaryTab, }); - navigate(`/market/pods/${newMode}/${defaultAction}`); + navigate(buildPath(`/market/pods/${newMode}/${defaultAction}`)); onMainSelectionChange?.(v); }, - [navigate, onMainSelectionChange, mainTab, secondaryTab], + [navigate, onMainSelectionChange, mainTab, secondaryTab, buildPath], ); const handleSecondaryChange = useCallback( @@ -79,13 +89,13 @@ export default function MarketModeSelect({ onMainSelectionChange, onSecondarySel }); if (v === "create") { - navigate(`/market/pods/${currentMode}/create`); + navigate(buildPath(`/market/pods/${currentMode}/create`)); } else if (v === "fill") { - navigate(`/market/pods/${currentMode}/fill`); + navigate(buildPath(`/market/pods/${currentMode}/fill`)); } onSecondarySelectionChange?.(v); }, - [mainTab, navigate, onSecondarySelectionChange, secondaryTab], + [mainTab, navigate, onSecondarySelectionChange, secondaryTab, buildPath], ); return ( diff --git a/src/pages/market/PodListingsTable.tsx b/src/pages/market/PodListingsTable.tsx index 35d750e2..7e68f948 100644 --- a/src/pages/market/PodListingsTable.tsx +++ b/src/pages/market/PodListingsTable.tsx @@ -7,6 +7,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import IconImage from "@/components/ui/IconImage"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/Table"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import usePodListings from "@/state/market/usePodListings"; import { useHarvestableIndex } from "@/state/useFieldData"; import useTokenData from "@/state/useTokenData"; @@ -17,8 +18,9 @@ import { useNavigate, useParams } from "react-router-dom"; export function PodListingsTable() { const { id: selectedListing } = useParams(); const BEAN = useTokenData().mainToken; + const { podMarketplaceId } = useBeanstalkMarket(); - const podListingsQuery = usePodListings(); + const podListingsQuery = usePodListings(podMarketplaceId); const podListings = podListingsQuery.data?.podListings; const harvestableIndex = useHarvestableIndex(); diff --git a/src/pages/market/PodOrdersTable.tsx b/src/pages/market/PodOrdersTable.tsx index 8022c737..c0a95398 100644 --- a/src/pages/market/PodOrdersTable.tsx +++ b/src/pages/market/PodOrdersTable.tsx @@ -7,6 +7,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com import IconImage from "@/components/ui/IconImage"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/Table"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import usePodOrders from "@/state/market/usePodOrders"; import useTokenData from "@/state/useTokenData"; import { formatter } from "@/utils/format"; @@ -16,7 +17,8 @@ import { useNavigate, useParams } from "react-router-dom"; export function PodOrdersTable() { const { id: selectedOrder } = useParams(); const BEAN = useTokenData().mainToken; - const podOrdersQuery = usePodOrders(); + const { podMarketplaceId } = useBeanstalkMarket(); + const podOrdersQuery = usePodOrders(podMarketplaceId); const podOrders = podOrdersQuery.data?.podOrders; const filteredOrders: NonNullable["data"]>["podOrders"] = []; diff --git a/src/pages/market/actions/CancelListing.tsx b/src/pages/market/actions/CancelListing.tsx index 032308f9..4af2a753 100644 --- a/src/pages/market/actions/CancelListing.tsx +++ b/src/pages/market/actions/CancelListing.tsx @@ -1,6 +1,7 @@ import { TokenValue } from "@/classes/TokenValue"; import SmartSubmitButton from "@/components/SmartSubmitButton"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import { beanstalkAbi } from "@/generated/contractHooks"; import { AllPodListingsQuery } from "@/generated/gql/pintostalk/graphql"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; @@ -21,6 +22,7 @@ export default function CancelListing({ listing }: CancelListingProps) { const diamondAddress = useProtocolAddress(); const account = useAccount(); const harvestableIndex = useHarvestableIndex(); + const { fieldId } = useBeanstalkMarket(); const navigate = useNavigate(); const queryClient = useQueryClient(); @@ -50,7 +52,7 @@ export default function CancelListing({ listing }: CancelListingProps) { abi: beanstalkAbi, functionName: "cancelPodListing", args: [ - 0n, // fieldId + fieldId, // fieldId TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), // index ], }); @@ -62,7 +64,7 @@ export default function CancelListing({ listing }: CancelListingProps) { } finally { setSubmitting(false); } - }, [listing, diamondAddress, setSubmitting, writeWithEstimateGas]); + }, [listing, diamondAddress, fieldId, setSubmitting, writeWithEstimateGas]); return ( <> diff --git a/src/pages/market/actions/CancelOrder.tsx b/src/pages/market/actions/CancelOrder.tsx index 65f80fd8..98aa5b56 100644 --- a/src/pages/market/actions/CancelOrder.tsx +++ b/src/pages/market/actions/CancelOrder.tsx @@ -4,6 +4,7 @@ import FarmBalanceToggle from "@/components/FarmBalanceToggle"; import SmartSubmitButton from "@/components/SmartSubmitButton"; import { Separator } from "@/components/ui/Separator"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import { beanstalkAbi } from "@/generated/contractHooks"; import { AllPodOrdersQuery } from "@/generated/gql/pintostalk/graphql"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; @@ -29,6 +30,7 @@ export default function CancelOrder({ order }: CancelOrderProps) { const diamondAddress = useProtocolAddress(); const { queryKeys: balanceQKs } = useFarmerBalances(); const account = useAccount(); + const { fieldId } = useBeanstalkMarket(); const navigate = useNavigate(); const [mode, toFarm, setMode] = useFarmTogglePreference(); @@ -71,7 +73,7 @@ export default function CancelOrder({ order }: CancelOrderProps) { args: [ { orderer: account.address, // account - fieldId: 0n, // fieldId + fieldId: fieldId, // fieldId pricePerPod, // pricePerPod maxPlaceInLine, // maxPlaceInLine minFillAmount, // minFillAmount @@ -87,7 +89,7 @@ export default function CancelOrder({ order }: CancelOrderProps) { } finally { setSubmitting(false); } - }, [order, diamondAddress, account, toFarm, mainToken, setSubmitting, writeWithEstimateGas]); + }, [order, diamondAddress, account, fieldId, toFarm, mainToken, setSubmitting, writeWithEstimateGas]); return ( <> diff --git a/src/pages/market/actions/CreateListing.tsx b/src/pages/market/actions/CreateListing.tsx index 5cb237f8..dbd1fbdf 100644 --- a/src/pages/market/actions/CreateListing.tsx +++ b/src/pages/market/actions/CreateListing.tsx @@ -11,9 +11,11 @@ import { MultiSlider, Slider } from "@/components/ui/Slider"; import { Switch } from "@/components/ui/Switch"; import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import { beanstalkAbi } from "@/generated/contractHooks"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import useTransaction from "@/hooks/useTransaction"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useFarmerField } from "@/state/useFarmerField"; import { useHarvestableIndex, usePodIndex } from "@/state/useFieldData"; import { useQueryKeys } from "@/state/useQueryKeys"; @@ -89,12 +91,24 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps const navigate = useNavigate(); const location = useLocation(); const farmerField = useFarmerField(); + const { isBeanstalkMarketplace, fieldId } = useBeanstalkMarket(); + const repayment = useFarmerBeanstalkRepayment(); const queryClient = useQueryClient(); const { allPodListings, allMarket, farmerMarket } = useQueryKeys({ account, harvestableIndex }); const allQK = useMemo(() => [allPodListings, allMarket, farmerMarket], [allPodListings, allMarket, farmerMarket]); - const userPlots = useMemo(() => farmerField?.plots || [], [farmerField?.plots]); + const userPlots = useMemo(() => { + if (isBeanstalkMarketplace) { + return repayment.pods.plots; + } + return farmerField?.plots || []; + }, [isBeanstalkMarketplace, farmerField?.plots, repayment.pods.plots]); + + const activeHarvestableIndex = useMemo( + () => (isBeanstalkMarketplace ? repayment.pods.harvestableIndex : harvestableIndex), + [isBeanstalkMarketplace, repayment.pods.harvestableIndex, harvestableIndex], + ); const [plot, setPlot] = useState([]); const [amount, setAmount] = useState(0); @@ -107,13 +121,18 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps const [successAmount, setSuccessAmount] = useState(null); const [successPrice, setSuccessPrice] = useState(null); const podIndex = usePodIndex(); - const maxExpiration = Number.parseInt(podIndex.toHuman()) - Number.parseInt(harvestableIndex.toHuman()) || 0; + const activePodIndex = useMemo( + () => (isBeanstalkMarketplace ? repayment.pods.podIndex : podIndex), + [isBeanstalkMarketplace, repayment.pods.podIndex, podIndex], + ); + const maxExpiration = + Number.parseInt(activePodIndex.toHuman()) - Number.parseInt(activeHarvestableIndex.toHuman()) || 0; const [expiresIn, setExpiresIn] = useState(null); const selectedExpiresIn = expiresIn ?? maxExpiration; const minFill = TokenValue.fromHuman(0.1, PODS.decimals); const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); - const plotPosition = plot.length > 0 ? plot[0].index.sub(harvestableIndex) : TV.ZERO; + const plotPosition = plot.length > 0 ? plot[0].index.sub(activeHarvestableIndex) : TV.ZERO; // Helper: Find nearest plot within 10% tolerance (for context menu prefill) const findNearestPlot = useCallback( @@ -125,7 +144,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps let minDistance = Infinity; for (const p of userPlots) { - const plotPos = Number(p.index.sub(harvestableIndex).toBigInt()); + const plotPos = Number(p.index.sub(activeHarvestableIndex).toBigInt()); const distance = Math.abs(plotPos - targetPosition); const tolerance = targetPosition * 0.1; // 10% tolerance @@ -137,19 +156,19 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps return nearestPlot; }, - [userPlots, harvestableIndex], + [userPlots, activeHarvestableIndex], ); // Calculate max pods based on selected plots OR all farmer plots const maxPodAmount = useMemo(() => { - const plotsToUse = plot.length > 0 ? plot : farmerField.plots; + const plotsToUse = plot.length > 0 ? plot : userPlots; if (plotsToUse.length === 0) return 0; return plotsToUse.reduce((sum, p) => sum + p.pods.toNumber(), 0); - }, [plot, farmerField.plots]); + }, [plot, userPlots]); // Calculate position range in line const positionInfo = useMemo(() => { - const plotsToUse = plot.length > 0 ? plot : farmerField.plots; + const plotsToUse = plot.length > 0 ? plot : userPlots; if (plotsToUse.length === 0) return null; const minIndex = plotsToUse.reduce((min, p) => (p.index.lt(min) ? p.index : min), plotsToUse[0].index); @@ -159,10 +178,10 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps }, plotsToUse[0].index); return { - start: minIndex.sub(harvestableIndex), - end: maxIndex.sub(harvestableIndex), + start: minIndex.sub(activeHarvestableIndex), + end: maxIndex.sub(activeHarvestableIndex), }; - }, [plot, farmerField.plots, harvestableIndex]); + }, [plot, userPlots, activeHarvestableIndex]); // Calculate selected pod range for PodLineGraph partial selection const selectedPodRange = useMemo(() => { @@ -243,7 +262,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps const scores = listingData .map((data) => { - const placeInLine = data.index.sub(harvestableIndex).toNumber(); + const placeInLine = data.index.sub(activeHarvestableIndex).toNumber(); // Use placeInLine in millions for consistent scaling return calculatePodScore(pricePerPod, placeInLine / MILLION); }) @@ -255,7 +274,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps const max = Math.max(...scores); return { min, max, isSingle: scores.length === 1 || min === max }; - }, [listingData, pricePerPod, harvestableIndex]); + }, [listingData, pricePerPod, activeHarvestableIndex]); // Notify parent component of selection changes useEffect(() => { @@ -324,7 +343,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps } // Find matching plots from farmer's field using string comparison - const validPlots = farmerField.plots.filter((p) => selectedPlotIndices.includes(p.index.toHuman())); + const validPlots = userPlots.filter((p) => selectedPlotIndices.includes(p.index.toHuman())); if (validPlots.length > 0) { // Mark this selection as processed @@ -348,7 +367,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps // Clean up location state to prevent re-selection on re-mount window.history.replaceState({}, document.title); } - }, [location.state, farmerField.plots, sortPlotsByIndex]); + }, [location.state, userPlots, sortPlotsByIndex]); // Prefill from context menu click - always update when new values arrive useEffect(() => { @@ -402,7 +421,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps prefillExpiresIn, maxExpiration, findNearestPlot, - harvestableIndex, + activeHarvestableIndex, navigate, location.pathname, sortPlotsByIndex, @@ -468,7 +487,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps const handlePlotGroupSelect = useCallback( (plotIndices: string[]) => { - const plotsInGroup = farmerField.plots.filter((p) => plotIndices.includes(p.index.toHuman())); + const plotsInGroup = userPlots.filter((p) => plotIndices.includes(p.index.toHuman())); if (plotsInGroup.length === 0) return; const allSelected = plotIndices.every((index) => plot.some((p) => p.index.toHuman() === index)); @@ -491,7 +510,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps handlePlotSelection(newPlots); } }, - [farmerField.plots, plot, handlePlotSelection], + [userPlots, plot, handlePlotSelection], ); // reset form and invalidate pod listing query @@ -538,7 +557,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps // pricePerPod should be encoded as uint24 with 6 decimals (0.5 * 1_000_000 = 500000) const encodedPricePerPod = pricePerPod ? Math.floor(pricePerPod * PRICE_PER_POD_CONFIG.DECIMAL_MULTIPLIER) : 0; const _expiresIn = TokenValue.fromHuman(selectedExpiresIn, PODS.decimals); - const maxHarvestableIndex = _expiresIn.add(harvestableIndex); + const maxHarvestableIndex = _expiresIn.add(activeHarvestableIndex); try { setSubmitting(true); toast.loading(`Creating ${listingData.length} Listing${listingData.length > 1 ? "s" : ""}...`); @@ -549,7 +568,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps for (const data of listingData) { const listingArgs = { lister: account, - fieldId: 0n, + fieldId: fieldId, index: data.index.toBigInt(), start: data.start.toBigInt(), podAmount: data.amount.toBigInt(), @@ -588,7 +607,8 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps pricePerPod, selectedExpiresIn, balanceTo, - harvestableIndex, + activeHarvestableIndex, + fieldId, minFill, plot, listingData, @@ -615,6 +635,9 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps selectedPodRange={selectedPodRange} label="My Pods In Line" onPlotGroupSelect={handlePlotGroupSelect} + plots={userPlots} + customHarvestableIndex={activeHarvestableIndex} + customPodIndex={activePodIndex} /> {/* Position in Line Display (below graph) */} @@ -757,7 +780,7 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps podAmount={amount} listingData={listingData} pricePerPod={pricePerPod} - harvestableIndex={harvestableIndex} + harvestableIndex={activeHarvestableIndex} /> )} (undefined); const [pricePerPod, setPricePerPod] = useState(PRICE_PER_POD_CONFIG.MIN); @@ -388,6 +394,7 @@ export default function CreateOrder() { minFill, fromMode, orderClipboard?.clipboard, + fieldId, ); advFarm.push(orderCallStruct); @@ -427,6 +434,7 @@ export default function CreateOrder() { tokenIn.symbol, podsOut, amountIn, + fieldId, ]); const swapDataNotReady = (shouldSwap && (!swapData || !swapBuild)) || !!swapQuery.error; @@ -438,8 +446,8 @@ export default function CreateOrder() { // Calculate orderRangeEnd for PodLineGraph overlay const orderRangeEnd = useMemo(() => { if (!maxPlaceInLine) return undefined; - return harvestableIndex.add(TokenValue.fromHuman(maxPlaceInLine.toString(), PODS.decimals)); - }, [maxPlaceInLine, harvestableIndex]); + return activeHarvestableIndex.add(TokenValue.fromHuman(maxPlaceInLine.toString(), PODS.decimals)); + }, [maxPlaceInLine, activeHarvestableIndex]); return (
diff --git a/src/pages/market/actions/FillListing.tsx b/src/pages/market/actions/FillListing.tsx index b00a9239..fbcde708 100644 --- a/src/pages/market/actions/FillListing.tsx +++ b/src/pages/market/actions/FillListing.tsx @@ -16,6 +16,7 @@ import { Separator } from "@/components/ui/Separator"; import { Slider } from "@/components/ui/Slider"; import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import fillPodListing from "@/encoders/fillPodListing"; import { beanstalkAbi } from "@/generated/contractHooks"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; @@ -30,6 +31,7 @@ import useTransaction from "@/hooks/useTransaction"; import usePriceImpactSummary from "@/hooks/wells/usePriceImpactSummary"; import usePodListings from "@/state/market/usePodListings"; import { useFarmerBalances } from "@/state/useFarmerBalances"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useFarmerPlotsQuery } from "@/state/useFarmerField"; import { useHarvestableIndex, usePodIndex } from "@/state/useFieldData"; import { useQueryKeys } from "@/state/useQueryKeys"; @@ -96,6 +98,8 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on const account = useAccount(); const farmerBalances = useFarmerBalances(); const harvestableIndex = useHarvestableIndex(); + const { podMarketplaceId, fieldId, isBeanstalkMarketplace } = useBeanstalkMarket(); + const repayment = useFarmerBeanstalkRepayment(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); // Use prop if provided, otherwise fall back to URL param @@ -119,7 +123,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on filterLP: true, }); - const podListings = usePodListings(); + const podListings = usePodListings(podMarketplaceId); const allListings = podListings.data; const [didSetPreferred, setDidSetPreferred] = useState(false); @@ -139,7 +143,9 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on // Place in line state const podIndex = usePodIndex(); - const maxPlace = Number.parseInt(podIndex.toHuman()) - Number.parseInt(harvestableIndex.toHuman()) || 0; + const activeHarvestableIndex = isBeanstalkMarketplace ? repayment.pods.harvestableIndex : harvestableIndex; + const activePodIndex = isBeanstalkMarketplace ? repayment.pods.podIndex : podIndex; + const maxPlace = Number.parseInt(activePodIndex.toHuman()) - Number.parseInt(activeHarvestableIndex.toHuman()) || 0; const [maxPlaceInLine, setMaxPlaceInLine] = useState(undefined); const [hasInitializedPlace, setHasInitializedPlace] = useState(false); @@ -213,12 +219,12 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on if (Number.isNaN(placeInLine) || placeInLine <= 0) { // Fallback to calculating from listing index if URL value is invalid const listingIndex = TokenValue.fromBlockchain(listing.index, PODS.decimals); - placeInLine = listingIndex.sub(harvestableIndex).toNumber(); + placeInLine = listingIndex.sub(activeHarvestableIndex).toNumber(); } } else { // Calculate listing's place in line from index (fallback for direct URL access) const listingIndex = TokenValue.fromBlockchain(listing.index, PODS.decimals); - placeInLine = listingIndex.sub(harvestableIndex).toNumber(); + placeInLine = listingIndex.sub(activeHarvestableIndex).toNumber(); } // Set max place in line to the place in line plus one (to include the current plot) @@ -226,7 +232,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on const maxPlaceValue = Math.min(maxPlace, Math.max(0, placeInLine + 1)); setMaxPlaceInLine(maxPlaceValue); setHasInitializedPlace(true); // Mark as initialized to prevent default value override - }, [listingId, allListings, maxPlace, mainToken.decimals, harvestableIndex, placeInLineFromUrl]); + }, [listingId, allListings, maxPlace, mainToken.decimals, activeHarvestableIndex, placeInLineFromUrl]); // Token selection handler with tracking const handleTokenSelection = useCallback( @@ -325,7 +331,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on // Calculate place in line boundary for filtering const maxPlaceIndex = maxPlaceInLine - ? harvestableIndex.add(TokenValue.fromHuman(maxPlaceInLine.toString(), PODS.decimals)) + ? activeHarvestableIndex.add(TokenValue.fromHuman(maxPlaceInLine.toString(), PODS.decimals)) : undefined; // Determine eligible listings (shown as green on graph) @@ -349,13 +355,13 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on // Calculate range overlay for visual feedback on graph const overlay = maxPlaceInLine ? { - start: harvestableIndex, - end: harvestableIndex.add(TokenValue.fromHuman(maxPlaceInLine.toString(), PODS.decimals)), + start: activeHarvestableIndex, + end: activeHarvestableIndex.add(TokenValue.fromHuman(maxPlaceInLine.toString(), PODS.decimals)), } : undefined; return { listingPlots: plots, eligibleListingIds: eligible, rangeOverlay: overlay }; - }, [allListings, maxPricePerPod, maxPlaceInLine, mainToken.decimals, harvestableIndex]); + }, [allListings, maxPricePerPod, maxPlaceInLine, mainToken.decimals, activeHarvestableIndex]); // Notify parent component when filter values change for chart highlighting useEffect(() => { @@ -468,7 +474,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on const { listing, beanAmount } = listingsToFill[0]; const listingPrice = TokenValue.fromBlockchain(listing.pricePerPod, mainToken.decimals); const podsFromListing = beanAmount.div(listingPrice); - const listingPlace = TokenValue.fromBlockchain(listing.index, PODS.decimals).sub(harvestableIndex); + const listingPlace = TokenValue.fromBlockchain(listing.index, PODS.decimals).sub(activeHarvestableIndex); return { avgPricePerPod: listingPrice, @@ -485,7 +491,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on for (const { listing, beanAmount } of listingsToFill) { const listingPrice = TokenValue.fromBlockchain(listing.pricePerPod, mainToken.decimals); const podsFromListing = beanAmount.div(listingPrice); - const listingPlace = TokenValue.fromBlockchain(listing.index, PODS.decimals).sub(harvestableIndex); + const listingPlace = TokenValue.fromBlockchain(listing.index, PODS.decimals).sub(activeHarvestableIndex); const pods = podsFromListing.toNumber(); totalValue += listingPrice.toNumber() * pods; @@ -501,7 +507,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on avgPlaceInLine: TokenValue.fromHuman(avgPlaceInLine, PODS.decimals), totalPods, }; - }, [listingsToFill, mainToken.decimals, harvestableIndex]); + }, [listingsToFill, mainToken.decimals, activeHarvestableIndex]); // Calculate total tokens needed to fill eligible listings const totalMainTokensToFill = useMemo(() => { @@ -574,7 +580,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on args: [ { lister: listing.farmer.id as Address, - fieldId: 0n, + fieldId: fieldId, index: TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), start: TokenValue.fromBlockchain(listing.start, PODS.decimals).toBigInt(), podAmount: TokenValue.fromBlockchain(listing.amount, PODS.decimals).toBigInt(), @@ -628,6 +634,7 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on beanAmount, FarmFromMode.INTERNAL, clipboard, + fieldId, ); advFarm.push(fillCall); diff --git a/src/pages/market/actions/FillOrder.tsx b/src/pages/market/actions/FillOrder.tsx index a75aa2af..b21b8695 100644 --- a/src/pages/market/actions/FillOrder.tsx +++ b/src/pages/market/actions/FillOrder.tsx @@ -10,12 +10,14 @@ import { Separator } from "@/components/ui/Separator"; import { MultiSlider } from "@/components/ui/Slider"; import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import { PODS } from "@/constants/internalTokens"; +import { useBeanstalkMarket } from "@/context/BeanstalkMarketContext"; import { beanstalkAbi } from "@/generated/contractHooks"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import { useFarmTogglePreference } from "@/hooks/useFarmTogglePreference"; import useTransaction from "@/hooks/useTransaction"; import usePodOrders from "@/state/market/usePodOrders"; import { useFarmerBalances } from "@/state/useFarmerBalances"; +import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; import { useFarmerField, useFarmerPlotsQuery } from "@/state/useFarmerField"; import { useHarvestableIndex, usePodIndex } from "@/state/useFieldData"; import { useQueryKeys } from "@/state/useQueryKeys"; @@ -32,7 +34,6 @@ import { useAccount } from "wagmi"; import CancelOrder from "./CancelOrder"; // Constants -const FIELD_ID = 0n; const MIN_PODS_THRESHOLD = 1; // Minimum pods required for order eligibility // Helper Functions @@ -84,6 +85,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { const podLine = podIndex.sub(harvestableIndex); const navigate = useNavigate(); const [searchParams] = useSearchParams(); + const { isBeanstalkMarketplace, fieldId, podMarketplaceId } = useBeanstalkMarket(); // Use prop if provided, otherwise fall back to URL param const orderId = selectedOrderId || searchParams.get("orderId"); @@ -118,9 +120,22 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { prevTotalCapacityRef.current = -1; // Reset to allow re-triggering range update }, [selectedOrderIds]); - const podOrders = usePodOrders(); + const podOrders = usePodOrders(podMarketplaceId); const allOrders = podOrders.data; const farmerField = useFarmerField(); + const repayment = useFarmerBeanstalkRepayment(); + + const userPlots = useMemo(() => { + if (isBeanstalkMarketplace) { + return repayment.pods.plots; + } + return farmerField?.plots || []; + }, [isBeanstalkMarketplace, farmerField?.plots, repayment.pods.plots]); + + const activeHarvestableIndex = useMemo( + () => (isBeanstalkMarketplace ? repayment.pods.harvestableIndex : harvestableIndex), + [isBeanstalkMarketplace, repayment.pods.harvestableIndex, harvestableIndex], + ); const { selectedOrders, orderPositions, totalCapacity } = useMemo(() => { if (!allOrders?.podOrders) return { selectedOrders: [], orderPositions: [], totalCapacity: 0 }; @@ -195,7 +210,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { if (!allOrders?.podOrders) return []; // Get farmer's frontmost pod position (lowest index) - const farmerPlots = farmerField.plots; + const farmerPlots = userPlots; const farmerFrontmostPodIndex = farmerPlots.length > 0 ? farmerPlots.reduce((min, plot) => (plot.index.lt(min) ? plot.index : min), farmerPlots[0].index) @@ -208,17 +223,19 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { } // Check if farmer has pods that can fill this order - // Order's maxPlaceInLine + harvestableIndex must be >= farmer's frontmost pod index + // Order's maxPlaceInLine + activeHarvestableIndex must be >= farmer's frontmost pod index if (!farmerFrontmostPodIndex) { return false; // No pods available } - const orderMaxPlaceIndex = harvestableIndex.add(TokenValue.fromBlockchain(order.maxPlaceInLine, PODS.decimals)); + const orderMaxPlaceIndex = activeHarvestableIndex.add( + TokenValue.fromBlockchain(order.maxPlaceInLine, PODS.decimals), + ); // Farmer's pod must be at or before the order's maxPlaceInLine position return farmerFrontmostPodIndex.lte(orderMaxPlaceIndex); }); - }, [allOrders?.podOrders, mainToken.decimals, podLine, farmerField.plots, harvestableIndex]); + }, [allOrders?.podOrders, mainToken.decimals, podLine, userPlots, activeHarvestableIndex]); useEffect(() => { if (totalCapacity !== prevTotalCapacityRef.current) { @@ -246,7 +263,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { return eligibleOrders.map((order) => { const orderMaxPlace = TokenValue.fromBlockchain(order.maxPlaceInLine, PODS.decimals); - const markerIndex = harvestableIndex.add(orderMaxPlace); + const markerIndex = activeHarvestableIndex.add(orderMaxPlace); return { index: markerIndex, @@ -255,7 +272,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { id: order.id, } as Plot; }); - }, [eligibleOrders, harvestableIndex]); + }, [eligibleOrders, activeHarvestableIndex]); const plotsForGraph = useMemo(() => { return orderMarkers; @@ -294,7 +311,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { }); const onSubmit = useCallback(async () => { - if (ordersToFill.length === 0 || !account || farmerField.plots.length === 0) { + if (ordersToFill.length === 0 || !account || userPlots.length === 0) { return; } @@ -324,7 +341,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { toast.loading(`Filling ${ordersToFill.length} Order${ordersToFill.length !== 1 ? "s" : ""}...`); // Sort farmer plots by index to use them in order (only sort once) - const sortedPlots = [...farmerField.plots].sort((a, b) => a.index.sub(b.index).toNumber()); + const sortedPlots = [...userPlots].sort((a, b) => a.index.sub(b.index).toNumber()); if (sortedPlots.length === 0) { throw new Error("No pods available to fill orders"); @@ -340,7 +357,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { for (const { order: orderToFill, amount: fillAmount } of ordersToFill) { let remainingAmount = fillAmount; - const orderMaxPlaceIndex = harvestableIndex.add( + const orderMaxPlaceIndex = activeHarvestableIndex.add( TokenValue.fromBlockchain(orderToFill.maxPlaceInLine, PODS.decimals), ); @@ -373,7 +390,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { // Create fillPodOrder call for this order with pod allocation from current plot const fillOrderArgs = { orderer: orderToFill.farmer.id as Address, - fieldId: FIELD_ID, + fieldId: fieldId, maxPlaceInLine: BigInt(orderToFill.maxPlaceInLine), pricePerPod: Number(orderToFill.pricePerPod), minFillAmount: BigInt(orderToFill.minFillAmount), @@ -430,13 +447,14 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { }, [ ordersToFill, account, - farmerField.plots, + userPlots, writeWithEstimateGas, setSubmitting, diamondAddress, amount, weightedAvgPricePerPod, - harvestableIndex, + activeHarvestableIndex, + fieldId, ]); const isOwnOrder = useMemo(() => { diff --git a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx index bbc11af1..73f9cfb1 100644 --- a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx +++ b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx @@ -1,6 +1,7 @@ import AddressInputField from "@/components/AddressInputField"; import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; import PodLineGraph from "@/components/PodLineGraph"; +import { Button } from "@/components/ui/Button"; import { Label } from "@/components/ui/Label"; import { MultiSlider } from "@/components/ui/Slider"; import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; @@ -9,6 +10,7 @@ import { computeTransferData, offsetToAbsoluteIndex } from "@/utils/podTransferU import { Plot } from "@/utils/types"; import { AnimatePresence, motion } from "framer-motion"; import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { Link } from "react-router-dom"; import { PodTransferData } from "../TransferBeanstalkPods"; interface StepOneProps { @@ -137,6 +139,14 @@ export default function StepOne({ return (
+
+ + +
- request(subgraphs[chainId].beanstalk, AllPodListingsDocument, { + queryKey: [...queryKey, { podMarketplaceId }], + queryFn: async () => { + const variables: { maxHarvestableIndex: string; skip: number; podMarketplace?: string } = { maxHarvestableIndex: harvestableIndex.toBigInt().toString(), skip: 0, - }), + }; + if (podMarketplaceId) variables.podMarketplace = podMarketplaceId; + return request(subgraphs[chainId].beanstalk, AllPodListingsDocument, variables); + }, enabled: harvestableIndex.gt(0), }); diff --git a/src/state/market/usePodOrders.ts b/src/state/market/usePodOrders.ts index c874da16..1db4ec90 100644 --- a/src/state/market/usePodOrders.ts +++ b/src/state/market/usePodOrders.ts @@ -5,17 +5,18 @@ import request from "graphql-request"; import { useChainId } from "wagmi"; import { useQueryKeys } from "../useQueryKeys"; -export default function usePodOrders() { +export default function usePodOrders(podMarketplaceId?: string) { const chainId = useChainId(); const { allPodOrders: queryKey } = useQueryKeys({ chainId }); const podOrders = useQuery({ - queryKey: queryKey, - queryFn: async () => - request(subgraphs[chainId].beanstalk, AllPodOrdersDocument, { - skip: 0, - }), + queryKey: [...queryKey, { podMarketplaceId }], + queryFn: async () => { + const variables: { skip: number; podMarketplace?: string } = { skip: 0 }; + if (podMarketplaceId) variables.podMarketplace = podMarketplaceId; + return request(subgraphs[chainId].beanstalk, AllPodOrdersDocument, variables); + }, }); return { From 674b768ca893aea1e17153572def59758f7fda55 Mon Sep 17 00:00:00 2001 From: feyyazcigim Date: Mon, 16 Feb 2026 20:00:14 +0300 Subject: [PATCH 19/22] chore: remove unecessary buttons from beanstalk pods send page --- src/pages/transfer/actions/beanstalk-pods/StepOne.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx index 73f9cfb1..bbc11af1 100644 --- a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx +++ b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx @@ -1,7 +1,6 @@ import AddressInputField from "@/components/AddressInputField"; import PintoAssetTransferNotice from "@/components/PintoAssetTransferNotice"; import PodLineGraph from "@/components/PodLineGraph"; -import { Button } from "@/components/ui/Button"; import { Label } from "@/components/ui/Label"; import { MultiSlider } from "@/components/ui/Slider"; import { useFarmerBeanstalkRepayment } from "@/state/useFarmerBeanstalkRepayment"; @@ -10,7 +9,6 @@ import { computeTransferData, offsetToAbsoluteIndex } from "@/utils/podTransferU import { Plot } from "@/utils/types"; import { AnimatePresence, motion } from "framer-motion"; import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Link } from "react-router-dom"; import { PodTransferData } from "../TransferBeanstalkPods"; interface StepOneProps { @@ -139,14 +137,6 @@ export default function StepOne({ return (
-
- - -
Date: Mon, 16 Feb 2026 20:07:35 +0300 Subject: [PATCH 20/22] chore: update fertilizer text order --- src/components/FertilizerCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/FertilizerCard.tsx b/src/components/FertilizerCard.tsx index 59e11261..4d0d7a68 100644 --- a/src/components/FertilizerCard.tsx +++ b/src/components/FertilizerCard.tsx @@ -40,11 +40,11 @@ export default function FertilizerCard({ {/* Fertilizer info */}
- {formatter.number(Number(maxBalance))} bsFERT - ID {formatter.number(Number(fertId))} + {sprouts} Sprouts + Humidity: {humidity}
- Sprouts: {sprouts} ยท Humidity: {humidity} + {formatter.number(Number(maxBalance))} bsFERT - ID {formatter.number(Number(fertId))}
From 267e9abb685e32a8dfde3143bfe6afd93feb234e Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 04:43:53 +0000 Subject: [PATCH 21/22] fix: make 'My Pods In Line' label black in pod transfer steps Co-authored-by: frijo --- src/pages/transfer/actions/beanstalk-pods/StepOne.tsx | 2 +- src/pages/transfer/actions/pods/StepOne.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx index bbc11af1..856c2cd2 100644 --- a/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx +++ b/src/pages/transfer/actions/beanstalk-pods/StepOne.tsx @@ -138,7 +138,7 @@ export default function StepOne({ return (
- + {/* Pod Line Graph Visualization */}
- + Date: Wed, 18 Feb 2026 04:59:30 +0000 Subject: [PATCH 22/22] fix: update text color hierarchy in Beanstalk dashboard - Section headers (My Beanstalk Silo/Pods/Fertilizer): text-pinto-light -> text-pinto-gray-6 (#6E6E6E, approx #6B7280) - Axis labels (pod line numbers): text-pinto-gray-4 -> text-pinto-gray-2 (#D9D9D9, approx #D1D5DB) - Disabled/unavailable actions already use disabled:text-pinto-gray-4 (#9C9C9C) - Secondary text (Earned, Fertilized labels) already use text-pinto-light (#9C9C9C) Co-authored-by: frijo --- src/components/BeanstalkStatField.tsx | 2 +- src/components/PodLineGraph.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/BeanstalkStatField.tsx b/src/components/BeanstalkStatField.tsx index e92574b1..167bd396 100644 --- a/src/components/BeanstalkStatField.tsx +++ b/src/components/BeanstalkStatField.tsx @@ -32,7 +32,7 @@ const BeanstalkStatField: React.FC = ({ return (
-
{title}
+
{title}
{actions && actions.length > 0 && (
{actions.map((action) => ( diff --git a/src/components/PodLineGraph.tsx b/src/components/PodLineGraph.tsx index ccf5093a..d7a9671e 100644 --- a/src/components/PodLineGraph.tsx +++ b/src/components/PodLineGraph.tsx @@ -637,7 +637,7 @@ export default function PodLineGraph({ return (