From fe71f61b29a834d3745cafe08f2cde5317cf05a8 Mon Sep 17 00:00:00 2001 From: fr1j0 Date: Wed, 28 Jan 2026 16:07:55 -0500 Subject: [PATCH 1/4] feat(market): implement batch marketplace operations and deposit conversion This comprehensive update introduces batch operations across the marketplace and silo systems, replacing individual farm() calls with optimized batch functions for improved gas efficiency and user experience. Marketplace Batch Operations: - Add batch create/fill/cancel functions for pod listings and orders - Implement batchCreatePodListing, batchFillPodListing encoders with tests - Implement batchCreatePodOrder, batchFillPodOrder encoders - Add batchCancelPodListing and batchCancelPodOrder functionality - Update Market.tsx with expanded 6-tab structure - Create MyListingsTable and MyOrdersTable components - Add bulk selection and cancellation UI with checkbox columns - Implement single and batch cancel flows with analytics tracking Silo Batch Conversion: - Add ConvertBatchFacetABI with batchConvert function - Implement batchConvert encoder with BatchConvertParams interface - Replace individual convert() calls with batchConvert in deposit combine - Update encodeGroupCombineCalls to use batchConvert - Add comprehensive documentation for convert flow UI/UX Improvements: - Replace cancel dialogs with immediate cancellation flow - Add Select All functionality to listing/order tables - Implement My Listings and My Orders sub-tabs under My Activity - Add GraphQL queries for user's own listings and orders - Improve button styling and interaction patterns - Add warning UI for unowned listings in batch operations Testing & Documentation: - Add unit tests for batchCreatePodListing encoder - Add unit tests for batchFillPodListing encoder - Document createSmartGroups and encodeGroupCombineCalls functions - Document farm() usage patterns and replacement strategies - Add comprehensive inline documentation for new functions Co-Authored-By: Claude Sonnet 4.5 --- .../encoders/batchCreatePodListing.test.ts | 139 ++++++ .../encoders/batchFillPodListing.test.ts | 225 +++++++++ src/components/CombineSelect.tsx | 10 +- src/constants/abi/diamondABI.ts | 404 ++++++++++++++++ src/encoders/batchCancelPodListing.ts | 26 + src/encoders/batchCancelPodOrder.ts | 34 ++ src/encoders/batchConvert.ts | 39 ++ src/encoders/batchCreatePodListing.ts | 41 ++ src/encoders/batchCreatePodOrder.ts | 39 ++ src/encoders/batchFillPodListing.ts | 49 ++ src/encoders/batchFillPodOrder.ts | 45 ++ src/encoders/index.ts | 18 + src/generated/contractHooks.ts | 456 ++++++++++++++++++ src/generated/gql/pintostalk/gql.ts | 12 + src/generated/gql/pintostalk/graphql.ts | 21 + src/lib/claim/depositUtils.ts | 83 +++- src/pages/Market.tsx | 26 +- src/pages/market/MyListingsTable.tsx | 317 ++++++++++++ src/pages/market/MyOrdersTable.tsx | 326 +++++++++++++ src/pages/market/PodListingsTable.tsx | 3 +- src/pages/market/actions/CancelListing.tsx | 86 +++- src/pages/market/actions/CancelOrder.tsx | 103 ++-- src/pages/market/actions/CreateListing.tsx | 44 +- src/pages/market/actions/FillListing.tsx | 54 +-- src/pages/market/actions/FillOrder.tsx | 63 ++- .../beanstalk/podmarket/MyPodListings.graphql | 23 + .../beanstalk/podmarket/MyPodOrders.graphql | 20 + src/state/market/useMyPodListings.ts | 47 ++ src/state/market/useMyPodOrders.ts | 41 ++ 29 files changed, 2622 insertions(+), 172 deletions(-) create mode 100644 src/__tests/encoders/batchCreatePodListing.test.ts create mode 100644 src/__tests/encoders/batchFillPodListing.test.ts create mode 100644 src/encoders/batchCancelPodListing.ts create mode 100644 src/encoders/batchCancelPodOrder.ts create mode 100644 src/encoders/batchConvert.ts create mode 100644 src/encoders/batchCreatePodListing.ts create mode 100644 src/encoders/batchCreatePodOrder.ts create mode 100644 src/encoders/batchFillPodListing.ts create mode 100644 src/encoders/batchFillPodOrder.ts create mode 100644 src/pages/market/MyListingsTable.tsx create mode 100644 src/pages/market/MyOrdersTable.tsx create mode 100644 src/queries/beanstalk/podmarket/MyPodListings.graphql create mode 100644 src/queries/beanstalk/podmarket/MyPodOrders.graphql create mode 100644 src/state/market/useMyPodListings.ts create mode 100644 src/state/market/useMyPodOrders.ts diff --git a/src/__tests/encoders/batchCreatePodListing.test.ts b/src/__tests/encoders/batchCreatePodListing.test.ts new file mode 100644 index 000000000..e1ae7a631 --- /dev/null +++ b/src/__tests/encoders/batchCreatePodListing.test.ts @@ -0,0 +1,139 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import batchCreatePodListing, { CreatePodListingParams } from "@/encoders/batchCreatePodListing"; +import { FarmToMode } from "@/utils/types"; +import { decodeFunctionData } from "viem"; +import { describe, expect, it } from "vitest"; + +const mockAddress = "0x1234567890abcdef1234567890abcdef12345678" as const; + +interface DecodedPodListing { + lister: string; + fieldId: bigint; + index: bigint; + start: bigint; + podAmount: bigint; + pricePerPod: number; + maxHarvestableIndex: bigint; + minFillAmount: bigint; + mode: number; +} + +function decodeResult(data: `0x${string}`) { + const decoded = decodeFunctionData({ + abi: diamondABI, + data, + }); + return { + functionName: decoded.functionName, + listings: decoded.args[0] as unknown as DecodedPodListing[], + }; +} + +function createMockListing(overrides: Partial = {}): CreatePodListingParams { + return { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromBlockchain("1000000", 6), + start: TokenValue.fromBlockchain("0", 6), + podAmount: TokenValue.fromBlockchain("5000000", 6), + pricePerPod: TokenValue.fromBlockchain("500000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("100000000000", 6), + minFillAmount: TokenValue.fromBlockchain("1000000", 6), + mode: FarmToMode.EXTERNAL, + ...overrides, + }; +} + +describe("batchCreatePodListing encoder", () => { + it("encodes a single listing", () => { + const listing = createMockListing(); + const result = batchCreatePodListing([listing]); + + expect(result).toMatch(/^0x/); + expect(result.length).toBeGreaterThan(10); + + const { functionName, listings } = decodeResult(result); + + expect(functionName).toBe("batchCreatePodListing"); + expect(listings).toHaveLength(1); + + const decodedListing = listings[0]; + expect(decodedListing.lister.toLowerCase()).toBe(mockAddress.toLowerCase()); + expect(decodedListing.fieldId).toBe(0n); + expect(decodedListing.index).toBe(listing.index.toBigInt()); + expect(decodedListing.start).toBe(listing.start.toBigInt()); + expect(decodedListing.podAmount).toBe(listing.podAmount.toBigInt()); + expect(decodedListing.pricePerPod).toBe(Number(listing.pricePerPod.toBigInt())); + expect(decodedListing.maxHarvestableIndex).toBe(listing.maxHarvestableIndex.toBigInt()); + expect(decodedListing.minFillAmount).toBe(listing.minFillAmount.toBigInt()); + expect(decodedListing.mode).toBe(Number(FarmToMode.EXTERNAL)); + }); + + it("encodes multiple listings", () => { + const listings = [ + createMockListing({ index: TokenValue.fromBlockchain("1000000", 6) }), + createMockListing({ index: TokenValue.fromBlockchain("2000000", 6) }), + createMockListing({ index: TokenValue.fromBlockchain("3000000", 6) }), + ]; + + const result = batchCreatePodListing(listings); + const { listings: decoded } = decodeResult(result); + + expect(decoded).toHaveLength(3); + expect(decoded[0].index).toBe(listings[0].index.toBigInt()); + expect(decoded[1].index).toBe(listings[1].index.toBigInt()); + expect(decoded[2].index).toBe(listings[2].index.toBigInt()); + }); + + it("converts pricePerPod to number (uint24)", () => { + const listing = createMockListing({ + pricePerPod: TokenValue.fromBlockchain("800000", 6), + }); + + const result = batchCreatePodListing([listing]); + const { listings } = decodeResult(result); + + expect(typeof listings[0].pricePerPod).toBe("number"); + expect(listings[0].pricePerPod).toBe(800000); + }); + + it("handles FarmToMode.INTERNAL", () => { + const listing = createMockListing({ mode: FarmToMode.INTERNAL }); + + const result = batchCreatePodListing([listing]); + const { listings } = decodeResult(result); + + expect(listings[0].mode).toBe(Number(FarmToMode.INTERNAL)); + }); + + it("converts TokenValue fields to BigInt", () => { + const listing = createMockListing({ + index: TokenValue.fromHuman("100", 6), + start: TokenValue.fromHuman("10", 6), + podAmount: TokenValue.fromHuman("50", 6), + maxHarvestableIndex: TokenValue.fromHuman("999999", 6), + minFillAmount: TokenValue.fromHuman("1", 6), + }); + + const result = batchCreatePodListing([listing]); + const { listings } = decodeResult(result); + + const d = listings[0]; + expect(d.index).toBe(100000000n); + expect(d.start).toBe(10000000n); + expect(d.podAmount).toBe(50000000n); + expect(d.maxHarvestableIndex).toBe(999999000000n); + expect(d.minFillAmount).toBe(1000000n); + }); + + it("preserves lister address", () => { + const customAddress = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as const; + const listing = createMockListing({ lister: customAddress }); + + const result = batchCreatePodListing([listing]); + const { listings } = decodeResult(result); + + expect(listings[0].lister.toLowerCase()).toBe(customAddress.toLowerCase()); + }); +}); diff --git a/src/__tests/encoders/batchFillPodListing.test.ts b/src/__tests/encoders/batchFillPodListing.test.ts new file mode 100644 index 000000000..417964b34 --- /dev/null +++ b/src/__tests/encoders/batchFillPodListing.test.ts @@ -0,0 +1,225 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import batchFillPodListing, { FillPodListingParams } from "@/encoders/batchFillPodListing"; +import { FarmFromMode, FarmToMode } from "@/utils/types"; +import { decodeFunctionData } from "viem"; +import { describe, expect, it } from "vitest"; + +const mockAddress = "0x1234567890abcdef1234567890abcdef12345678" as const; + +interface DecodedFillParams { + podListing: { + lister: string; + fieldId: bigint; + index: bigint; + start: bigint; + podAmount: bigint; + pricePerPod: number; + maxHarvestableIndex: bigint; + minFillAmount: bigint; + mode: number; + }; + beanAmount: bigint; + mode: number; +} + +function decodeResult(data: `0x${string}`) { + const decoded = decodeFunctionData({ + abi: diamondABI, + data, + }); + return { + functionName: decoded.functionName, + params: decoded.args[0] as unknown as DecodedFillParams[], + }; +} + +function createMockFillParams(overrides: Partial = {}): FillPodListingParams { + return { + podListing: { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromBlockchain("1000000", 6), + start: TokenValue.fromBlockchain("0", 6), + podAmount: TokenValue.fromBlockchain("5000000", 6), + pricePerPod: TokenValue.fromBlockchain("500000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("100000000000", 6), + minFillAmount: TokenValue.fromBlockchain("1000000", 6), + mode: FarmToMode.EXTERNAL, + ...overrides?.podListing, + }, + beanAmount: TokenValue.fromBlockchain("2000000", 6), + mode: FarmFromMode.EXTERNAL, + ...("beanAmount" in overrides ? { beanAmount: overrides.beanAmount } : {}), + ...("mode" in overrides && overrides.mode !== undefined ? { mode: overrides.mode } : {}), + }; +} + +describe("batchFillPodListing encoder", () => { + it("encodes a single fill", () => { + const fill = createMockFillParams(); + const result = batchFillPodListing([fill]); + + expect(result).toMatch(/^0x/); + expect(result.length).toBeGreaterThan(10); + + const { functionName, params } = decodeResult(result); + + expect(functionName).toBe("batchFillPodListing"); + expect(params).toHaveLength(1); + + const decoded = params[0]; + expect(decoded.podListing.lister.toLowerCase()).toBe(mockAddress.toLowerCase()); + expect(decoded.podListing.fieldId).toBe(0n); + expect(decoded.podListing.index).toBe(fill.podListing.index.toBigInt()); + expect(decoded.podListing.start).toBe(fill.podListing.start.toBigInt()); + expect(decoded.podListing.podAmount).toBe(fill.podListing.podAmount.toBigInt()); + expect(decoded.podListing.pricePerPod).toBe(Number(fill.podListing.pricePerPod.toBigInt())); + expect(decoded.podListing.maxHarvestableIndex).toBe(fill.podListing.maxHarvestableIndex.toBigInt()); + expect(decoded.podListing.minFillAmount).toBe(fill.podListing.minFillAmount.toBigInt()); + expect(decoded.podListing.mode).toBe(Number(FarmToMode.EXTERNAL)); + expect(decoded.beanAmount).toBe(fill.beanAmount.toBigInt()); + expect(decoded.mode).toBe(Number(FarmFromMode.EXTERNAL)); + }); + + it("encodes multiple fills", () => { + const fills: FillPodListingParams[] = [ + createMockFillParams(), + createMockFillParams({ + podListing: { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromBlockchain("2000000", 6), + start: TokenValue.fromBlockchain("0", 6), + podAmount: TokenValue.fromBlockchain("3000000", 6), + pricePerPod: TokenValue.fromBlockchain("600000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("100000000000", 6), + minFillAmount: TokenValue.fromBlockchain("500000", 6), + mode: FarmToMode.EXTERNAL, + }, + beanAmount: TokenValue.fromBlockchain("1500000", 6), + mode: FarmFromMode.INTERNAL, + }), + createMockFillParams({ + podListing: { + lister: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as const, + fieldId: 0n, + index: TokenValue.fromBlockchain("3000000", 6), + start: TokenValue.fromBlockchain("100000", 6), + podAmount: TokenValue.fromBlockchain("8000000", 6), + pricePerPod: TokenValue.fromBlockchain("450000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("200000000000", 6), + minFillAmount: TokenValue.fromBlockchain("2000000", 6), + mode: FarmToMode.INTERNAL, + }, + beanAmount: TokenValue.fromBlockchain("4000000", 6), + mode: FarmFromMode.EXTERNAL, + }), + ]; + + const result = batchFillPodListing(fills); + const { params } = decodeResult(result); + + expect(params).toHaveLength(3); + expect(params[0].podListing.index).toBe(fills[0].podListing.index.toBigInt()); + expect(params[1].podListing.index).toBe(fills[1].podListing.index.toBigInt()); + expect(params[2].podListing.index).toBe(fills[2].podListing.index.toBigInt()); + expect(params[1].beanAmount).toBe(fills[1].beanAmount.toBigInt()); + expect(params[2].podListing.lister.toLowerCase()).toBe("0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"); + }); + + it("converts pricePerPod to number (uint24)", () => { + const fill = createMockFillParams({ + podListing: { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromBlockchain("1000000", 6), + start: TokenValue.fromBlockchain("0", 6), + podAmount: TokenValue.fromBlockchain("5000000", 6), + pricePerPod: TokenValue.fromBlockchain("800000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("100000000000", 6), + minFillAmount: TokenValue.fromBlockchain("1000000", 6), + mode: FarmToMode.EXTERNAL, + }, + }); + + const result = batchFillPodListing([fill]); + const { params } = decodeResult(result); + + expect(typeof params[0].podListing.pricePerPod).toBe("number"); + expect(params[0].podListing.pricePerPod).toBe(800000); + }); + + it("handles FarmFromMode.INTERNAL", () => { + const fill = createMockFillParams({ + podListing: { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromBlockchain("1000000", 6), + start: TokenValue.fromBlockchain("0", 6), + podAmount: TokenValue.fromBlockchain("5000000", 6), + pricePerPod: TokenValue.fromBlockchain("500000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("100000000000", 6), + minFillAmount: TokenValue.fromBlockchain("1000000", 6), + mode: FarmToMode.EXTERNAL, + }, + beanAmount: TokenValue.fromBlockchain("2000000", 6), + mode: FarmFromMode.INTERNAL, + }); + + const result = batchFillPodListing([fill]); + const { params } = decodeResult(result); + + expect(params[0].mode).toBe(Number(FarmFromMode.INTERNAL)); + }); + + it("handles FarmToMode on podListing.mode", () => { + const fill = createMockFillParams({ + podListing: { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromBlockchain("1000000", 6), + start: TokenValue.fromBlockchain("0", 6), + podAmount: TokenValue.fromBlockchain("5000000", 6), + pricePerPod: TokenValue.fromBlockchain("500000", 6), + maxHarvestableIndex: TokenValue.fromBlockchain("100000000000", 6), + minFillAmount: TokenValue.fromBlockchain("1000000", 6), + mode: FarmToMode.INTERNAL, + }, + }); + + const result = batchFillPodListing([fill]); + const { params } = decodeResult(result); + + expect(params[0].podListing.mode).toBe(Number(FarmToMode.INTERNAL)); + }); + + it("converts TokenValue fields to BigInt correctly", () => { + const fill = createMockFillParams({ + podListing: { + lister: mockAddress, + fieldId: 0n, + index: TokenValue.fromHuman("100", 6), + start: TokenValue.fromHuman("10", 6), + podAmount: TokenValue.fromHuman("50", 6), + pricePerPod: TokenValue.fromBlockchain("500000", 6), + maxHarvestableIndex: TokenValue.fromHuman("999999", 6), + minFillAmount: TokenValue.fromHuman("1", 6), + mode: FarmToMode.EXTERNAL, + }, + beanAmount: TokenValue.fromHuman("25", 6), + mode: FarmFromMode.EXTERNAL, + }); + + const result = batchFillPodListing([fill]); + const { params } = decodeResult(result); + + const d = params[0]; + expect(d.podListing.index).toBe(100000000n); + expect(d.podListing.start).toBe(10000000n); + expect(d.podListing.podAmount).toBe(50000000n); + expect(d.podListing.maxHarvestableIndex).toBe(999999000000n); + expect(d.podListing.minFillAmount).toBe(1000000n); + expect(d.beanAmount).toBe(25000000n); + }); +}); diff --git a/src/components/CombineSelect.tsx b/src/components/CombineSelect.tsx index a3bedf2e6..0771655b4 100644 --- a/src/components/CombineSelect.tsx +++ b/src/components/CombineSelect.tsx @@ -63,13 +63,19 @@ export default function CombineSelect({ setTransferData, token, disabled }: Comb setSubmitting(true); toast.loading("Combining deposits..."); - const encodedCalls = encodeGroupCombineCalls(validGroups, token, farmerDeposits.deposits); + const encodedCall = encodeGroupCombineCalls(validGroups, token, farmerDeposits.deposits); + + if (!encodedCall) { + toast.error("No deposits to combine"); + setSubmitting(false); + return; + } await writeWithEstimateGas({ address: protocolAddress, abi: diamondABI, functionName: "farm", - args: [encodedCalls], + args: [[encodedCall]], }); setOpen(false); diff --git a/src/constants/abi/diamondABI.ts b/src/constants/abi/diamondABI.ts index dc0414c6e..27d93216d 100644 --- a/src/constants/abi/diamondABI.ts +++ b/src/constants/abi/diamondABI.ts @@ -10933,4 +10933,408 @@ export const diamondABI = [ stateMutability: "nonpayable", type: "function", }, + { + inputs: [ + { + components: [ + { + internalType: "uint256", + name: "fieldId", + type: "uint256", + }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + ], + internalType: "struct CancelPodListingParams[]", + name: "params", + type: "tuple[]", + }, + ], + name: "batchCancelPodListing", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "orderer", + type: "address", + }, + { + internalType: "uint256", + name: "fieldId", + type: "uint256", + }, + { + internalType: "uint24", + name: "pricePerPod", + type: "uint24", + }, + { + internalType: "uint256", + name: "maxPlaceInLine", + type: "uint256", + }, + { + internalType: "uint256", + name: "minFillAmount", + type: "uint256", + }, + ], + internalType: "struct Order.PodOrder[]", + name: "podOrders", + type: "tuple[]", + }, + { + internalType: "enum LibTransfer.To", + name: "mode", + type: "uint8", + }, + ], + name: "batchCancelPodOrder", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "lister", + type: "address", + }, + { + internalType: "uint256", + name: "fieldId", + type: "uint256", + }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "uint256", + name: "start", + type: "uint256", + }, + { + internalType: "uint256", + name: "podAmount", + type: "uint256", + }, + { + internalType: "uint24", + name: "pricePerPod", + type: "uint24", + }, + { + internalType: "uint256", + name: "maxHarvestableIndex", + type: "uint256", + }, + { + internalType: "uint256", + name: "minFillAmount", + type: "uint256", + }, + { + internalType: "enum LibTransfer.To", + name: "mode", + type: "uint8", + }, + ], + internalType: "struct Listing.PodListing[]", + name: "podListings", + type: "tuple[]", + }, + ], + name: "batchCreatePodListing", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "lister", + type: "address", + }, + { + internalType: "uint256", + name: "fieldId", + type: "uint256", + }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "uint256", + name: "start", + type: "uint256", + }, + { + internalType: "uint256", + name: "podAmount", + type: "uint256", + }, + { + internalType: "uint24", + name: "pricePerPod", + type: "uint24", + }, + { + internalType: "uint256", + name: "maxHarvestableIndex", + type: "uint256", + }, + { + internalType: "uint256", + name: "minFillAmount", + type: "uint256", + }, + { + internalType: "enum LibTransfer.To", + name: "mode", + type: "uint8", + }, + ], + internalType: "struct Listing.PodListing", + name: "podListing", + type: "tuple", + }, + { + internalType: "uint256", + name: "beanAmount", + type: "uint256", + }, + { + internalType: "enum LibTransfer.From", + name: "mode", + type: "uint8", + }, + ], + internalType: "struct FillPodListingParams[]", + name: "params", + type: "tuple[]", + }, + ], + name: "batchFillPodListing", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "orderer", + type: "address", + }, + { + internalType: "uint256", + name: "fieldId", + type: "uint256", + }, + { + internalType: "uint24", + name: "pricePerPod", + type: "uint24", + }, + { + internalType: "uint256", + name: "maxPlaceInLine", + type: "uint256", + }, + { + internalType: "uint256", + name: "minFillAmount", + type: "uint256", + }, + ], + internalType: "struct Order.PodOrder", + name: "podOrder", + type: "tuple", + }, + { + internalType: "uint256", + name: "beanAmount", + type: "uint256", + }, + { + internalType: "enum LibTransfer.From", + name: "mode", + type: "uint8", + }, + ], + internalType: "struct CreatePodOrderParams[]", + name: "params", + type: "tuple[]", + }, + ], + name: "batchCreatePodOrder", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "orderer", + type: "address", + }, + { + internalType: "uint256", + name: "fieldId", + type: "uint256", + }, + { + internalType: "uint24", + name: "pricePerPod", + type: "uint24", + }, + { + internalType: "uint256", + name: "maxPlaceInLine", + type: "uint256", + }, + { + internalType: "uint256", + name: "minFillAmount", + type: "uint256", + }, + ], + internalType: "struct Order.PodOrder", + name: "podOrder", + type: "tuple", + }, + { + internalType: "uint256", + name: "index", + type: "uint256", + }, + { + internalType: "uint256", + name: "start", + type: "uint256", + }, + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "enum LibTransfer.To", + name: "mode", + type: "uint8", + }, + ], + internalType: "struct FillPodOrderParams[]", + name: "params", + type: "tuple[]", + }, + ], + name: "batchFillPodOrder", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "bytes", + name: "convertData", + type: "bytes", + }, + { + internalType: "int96[]", + name: "stems", + type: "int96[]", + }, + { + internalType: "uint256[]", + name: "amounts", + type: "uint256[]", + }, + { + internalType: "int256", + name: "grownStalkSlippage", + type: "int256", + }, + ], + internalType: "struct ConvertBatchFacet.ConvertParams[]", + name: "converts", + type: "tuple[]", + }, + ], + name: "batchConvert", + outputs: [ + { + components: [ + { + internalType: "uint8", + name: "convertKind", + type: "uint8", + }, + { + internalType: "int96", + name: "toStem", + type: "int96", + }, + { + internalType: "uint256", + name: "fromAmount", + type: "uint256", + }, + { + internalType: "uint256", + name: "toAmount", + type: "uint256", + }, + { + internalType: "uint256", + name: "fromBdv", + type: "uint256", + }, + { + internalType: "uint256", + name: "toBdv", + type: "uint256", + }, + ], + internalType: "struct ConvertBatchFacet.ConvertOutput[]", + name: "convertOutputs", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, ] as const; diff --git a/src/encoders/batchCancelPodListing.ts b/src/encoders/batchCancelPodListing.ts new file mode 100644 index 000000000..345dc6ad3 --- /dev/null +++ b/src/encoders/batchCancelPodListing.ts @@ -0,0 +1,26 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import { encodeFunctionData } from "viem"; + +export interface CancelPodListingParams { + fieldId: bigint; + index: TokenValue; +} + +/** + * Encodes a batch cancel pod listing transaction + * @param params Array of listing parameters to cancel + * @returns Encoded function data for batchCancelPodListing + */ +export default function batchCancelPodListing(params: CancelPodListingParams[]): `0x${string}` { + const args = params.map((p) => ({ + fieldId: p.fieldId, + index: p.index.toBigInt(), + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchCancelPodListing", + args: [args], + }); +} diff --git a/src/encoders/batchCancelPodOrder.ts b/src/encoders/batchCancelPodOrder.ts new file mode 100644 index 000000000..8cae47f16 --- /dev/null +++ b/src/encoders/batchCancelPodOrder.ts @@ -0,0 +1,34 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import { FarmToMode } from "@/utils/types"; +import { Address, encodeFunctionData } from "viem"; + +export interface CancelPodOrderParams { + orderer: Address; + fieldId: bigint; + pricePerPod: TokenValue; + maxPlaceInLine: TokenValue; + minFillAmount: TokenValue; +} + +/** + * Encodes a batch cancel pod order transaction + * @param orders Array of order parameters to cancel + * @param mode The destination mode for returned tokens (INTERNAL or EXTERNAL) + * @returns Encoded function data for batchCancelPodOrder + */ +export default function batchCancelPodOrder(orders: CancelPodOrderParams[], mode: FarmToMode): `0x${string}` { + const args = orders.map((o) => ({ + orderer: o.orderer, + fieldId: o.fieldId, + pricePerPod: Number(o.pricePerPod.toBigInt()), // uint24 expects number, not bigint + maxPlaceInLine: o.maxPlaceInLine.toBigInt(), + minFillAmount: o.minFillAmount.toBigInt(), + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchCancelPodOrder", + args: [args, Number(mode)], + }); +} diff --git a/src/encoders/batchConvert.ts b/src/encoders/batchConvert.ts new file mode 100644 index 000000000..bff84a791 --- /dev/null +++ b/src/encoders/batchConvert.ts @@ -0,0 +1,39 @@ +import { diamondABI } from "@/constants/abi/diamondABI"; +import { HashString } from "@/utils/types.generic"; +import { encodeFunctionData } from "viem"; + +/** + * Parameters for a single convert operation within a batch. + * Each entry represents one deposit group to combine via same-token convert. + */ +export interface BatchConvertParams { + /** Encoded conversion parameters (from calculateConvertData) */ + convertData: HashString; + /** Deposit stems to convert (int96[]) */ + stems: bigint[]; + /** Amounts per deposit to convert (uint256[]) */ + amounts: bigint[]; + /** Grown stalk slippage tolerance (int256). Use 0n for same-token combines. */ + grownStalkSlippage: bigint; +} + +/** + * Encodes a batch convert transaction that combines multiple convert operations + * into a single on-chain call. Used for deposit combine/sort optimization. + * @param params Array of convert parameters (one per deposit group) + * @returns Encoded function data for batchConvert + */ +export default function batchConvert(params: BatchConvertParams[]): `0x${string}` { + const args = params.map((p) => ({ + convertData: p.convertData, + stems: p.stems, + amounts: p.amounts, + grownStalkSlippage: p.grownStalkSlippage, + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchConvert", + args: [args], + }); +} diff --git a/src/encoders/batchCreatePodListing.ts b/src/encoders/batchCreatePodListing.ts new file mode 100644 index 000000000..4b67266aa --- /dev/null +++ b/src/encoders/batchCreatePodListing.ts @@ -0,0 +1,41 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import { FarmToMode } from "@/utils/types"; +import { Address, encodeFunctionData } from "viem"; + +export interface CreatePodListingParams { + lister: Address; + fieldId: bigint; + index: TokenValue; + start: TokenValue; + podAmount: TokenValue; + pricePerPod: TokenValue; + maxHarvestableIndex: TokenValue; + minFillAmount: TokenValue; + mode: FarmToMode; +} + +/** + * Encodes a batch create pod listing transaction + * @param listings Array of listing parameters to create + * @returns Encoded function data for batchCreatePodListing + */ +export default function batchCreatePodListing(listings: CreatePodListingParams[]): `0x${string}` { + const args = listings.map((l) => ({ + lister: l.lister, + fieldId: l.fieldId, + index: l.index.toBigInt(), + start: l.start.toBigInt(), + podAmount: l.podAmount.toBigInt(), + pricePerPod: Number(l.pricePerPod.toBigInt()), // uint24 expects number + maxHarvestableIndex: l.maxHarvestableIndex.toBigInt(), + minFillAmount: l.minFillAmount.toBigInt(), + mode: Number(l.mode), + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchCreatePodListing", + args: [args], + }); +} diff --git a/src/encoders/batchCreatePodOrder.ts b/src/encoders/batchCreatePodOrder.ts new file mode 100644 index 000000000..f3159dc79 --- /dev/null +++ b/src/encoders/batchCreatePodOrder.ts @@ -0,0 +1,39 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import { FarmFromMode } from "@/utils/types"; +import { Address, encodeFunctionData } from "viem"; + +export interface CreatePodOrderParams { + orderer: Address; + fieldId: bigint; + pricePerPod: TokenValue; + maxPlaceInLine: TokenValue; + minFillAmount: TokenValue; + beanAmount: TokenValue; + mode: FarmFromMode; +} + +/** + * Encodes a batch create pod order transaction + * @param orders Array of order parameters to create + * @returns Encoded function data for batchCreatePodOrder + */ +export default function batchCreatePodOrder(orders: CreatePodOrderParams[]): `0x${string}` { + const args = orders.map((o) => ({ + podOrder: { + orderer: o.orderer, + fieldId: o.fieldId, + pricePerPod: Number(o.pricePerPod.toBigInt()), // uint24 expects number + maxPlaceInLine: o.maxPlaceInLine.toBigInt(), + minFillAmount: o.minFillAmount.toBigInt(), + }, + beanAmount: o.beanAmount.toBigInt(), + mode: Number(o.mode), + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchCreatePodOrder", + args: [args], + }); +} diff --git a/src/encoders/batchFillPodListing.ts b/src/encoders/batchFillPodListing.ts new file mode 100644 index 000000000..949678c3f --- /dev/null +++ b/src/encoders/batchFillPodListing.ts @@ -0,0 +1,49 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import { FarmFromMode, FarmToMode } from "@/utils/types"; +import { Address, encodeFunctionData } from "viem"; + +export interface FillPodListingParams { + podListing: { + lister: Address; + fieldId: bigint; + index: TokenValue; + start: TokenValue; + podAmount: TokenValue; + pricePerPod: TokenValue; + maxHarvestableIndex: TokenValue; + minFillAmount: TokenValue; + mode: FarmToMode; + }; + beanAmount: TokenValue; + mode: FarmFromMode; +} + +/** + * Encodes a batch fill pod listing transaction + * @param params Array of fill listing parameters + * @returns Encoded function data for batchFillPodListing + */ +export default function batchFillPodListing(params: FillPodListingParams[]): `0x${string}` { + const args = params.map((p) => ({ + podListing: { + lister: p.podListing.lister, + fieldId: p.podListing.fieldId, + index: p.podListing.index.toBigInt(), + start: p.podListing.start.toBigInt(), + podAmount: p.podListing.podAmount.toBigInt(), + pricePerPod: Number(p.podListing.pricePerPod.toBigInt()), // uint24 + maxHarvestableIndex: p.podListing.maxHarvestableIndex.toBigInt(), + minFillAmount: p.podListing.minFillAmount.toBigInt(), + mode: Number(p.podListing.mode), + }, + beanAmount: p.beanAmount.toBigInt(), + mode: Number(p.mode), + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchFillPodListing", + args: [args], + }); +} diff --git a/src/encoders/batchFillPodOrder.ts b/src/encoders/batchFillPodOrder.ts new file mode 100644 index 000000000..a24c9aa97 --- /dev/null +++ b/src/encoders/batchFillPodOrder.ts @@ -0,0 +1,45 @@ +import { TokenValue } from "@/classes/TokenValue"; +import { diamondABI } from "@/constants/abi/diamondABI"; +import { FarmToMode } from "@/utils/types"; +import { Address, encodeFunctionData } from "viem"; + +export interface FillPodOrderParams { + podOrder: { + orderer: Address; + fieldId: bigint; + pricePerPod: TokenValue; + maxPlaceInLine: TokenValue; + minFillAmount: TokenValue; + }; + index: TokenValue; + start: TokenValue; + amount: TokenValue; + mode: FarmToMode; +} + +/** + * Encodes a batch fill pod order transaction + * @param params Array of fill order parameters + * @returns Encoded function data for batchFillPodOrder + */ +export default function batchFillPodOrder(params: FillPodOrderParams[]): `0x${string}` { + const args = params.map((p) => ({ + podOrder: { + orderer: p.podOrder.orderer, + fieldId: p.podOrder.fieldId, + pricePerPod: Number(p.podOrder.pricePerPod.toBigInt()), // uint24 + maxPlaceInLine: p.podOrder.maxPlaceInLine.toBigInt(), + minFillAmount: p.podOrder.minFillAmount.toBigInt(), + }, + index: p.index.toBigInt(), + start: p.start.toBigInt(), + amount: p.amount.toBigInt(), + mode: Number(p.mode), + })); + + return encodeFunctionData({ + abi: diamondABI, + functionName: "batchFillPodOrder", + args: [args], + }); +} diff --git a/src/encoders/index.ts b/src/encoders/index.ts index 8cc3ec41c..c5b22a673 100644 --- a/src/encoders/index.ts +++ b/src/encoders/index.ts @@ -1,4 +1,11 @@ import advancedPipe from "./advancedPipe"; +import batchCancelPodListing from "./batchCancelPodListing"; +import batchCancelPodOrder from "./batchCancelPodOrder"; +import batchConvert from "./batchConvert"; +import batchCreatePodListing from "./batchCreatePodListing"; +import batchCreatePodOrder from "./batchCreatePodOrder"; +import batchFillPodListing from "./batchFillPodListing"; +import batchFillPodOrder from "./batchFillPodOrder"; import erc20Approve from "./erc20Approve"; import erc20BalanceOf from "./erc20BalanceOf"; import erc20Transfer from "./erc20Transfer"; @@ -34,6 +41,16 @@ import wrapEth from "./wrapEth"; const encoders = { advancedPipe, + // marketplace batch operations + market: { + batchCancelPodListing, + batchCancelPodOrder, + batchCreatePodListing, + batchCreatePodOrder, + batchFillPodListing, + batchFillPodOrder, + }, + // junction junction: { check: junctionCheck, @@ -47,6 +64,7 @@ const encoders = { silo: { pipelineConvert, convert, + batchConvert, withdraw: siloWithdraw, getMaxAmountIn, getAmountOut, diff --git a/src/generated/contractHooks.ts b/src/generated/contractHooks.ts index 26efd6000..7b7ce9be3 100644 --- a/src/generated/contractHooks.ts +++ b/src/generated/contractHooks.ts @@ -6214,6 +6214,238 @@ export const beanstalkAbi = [ ], stateMutability: 'nonpayable', }, + { + type: 'function', + inputs: [ + { + name: 'params', + internalType: 'struct CancelPodListingParams[]', + type: 'tuple[]', + components: [ + { name: 'fieldId', internalType: 'uint256', type: 'uint256' }, + { name: 'index', internalType: 'uint256', type: 'uint256' }, + ], + }, + ], + name: 'batchCancelPodListing', + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + inputs: [ + { + name: 'podOrders', + internalType: 'struct Order.PodOrder[]', + type: 'tuple[]', + components: [ + { name: 'orderer', internalType: 'address', type: 'address' }, + { name: 'fieldId', internalType: 'uint256', type: 'uint256' }, + { name: 'pricePerPod', internalType: 'uint24', type: 'uint24' }, + { name: 'maxPlaceInLine', internalType: 'uint256', type: 'uint256' }, + { name: 'minFillAmount', internalType: 'uint256', type: 'uint256' }, + ], + }, + { name: 'mode', internalType: 'enum LibTransfer.To', type: 'uint8' }, + ], + name: 'batchCancelPodOrder', + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + inputs: [ + { + name: 'podListings', + internalType: 'struct Listing.PodListing[]', + type: 'tuple[]', + components: [ + { name: 'lister', internalType: 'address', type: 'address' }, + { name: 'fieldId', internalType: 'uint256', type: 'uint256' }, + { name: 'index', internalType: 'uint256', type: 'uint256' }, + { name: 'start', internalType: 'uint256', type: 'uint256' }, + { name: 'podAmount', internalType: 'uint256', type: 'uint256' }, + { name: 'pricePerPod', internalType: 'uint24', type: 'uint24' }, + { + name: 'maxHarvestableIndex', + internalType: 'uint256', + type: 'uint256', + }, + { name: 'minFillAmount', internalType: 'uint256', type: 'uint256' }, + { name: 'mode', internalType: 'enum LibTransfer.To', type: 'uint8' }, + ], + }, + ], + name: 'batchCreatePodListing', + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + inputs: [ + { + name: 'params', + internalType: 'struct FillPodListingParams[]', + type: 'tuple[]', + components: [ + { + name: 'podListing', + internalType: 'struct Listing.PodListing', + type: 'tuple', + components: [ + { name: 'lister', internalType: 'address', type: 'address' }, + { name: 'fieldId', internalType: 'uint256', type: 'uint256' }, + { name: 'index', internalType: 'uint256', type: 'uint256' }, + { name: 'start', internalType: 'uint256', type: 'uint256' }, + { name: 'podAmount', internalType: 'uint256', type: 'uint256' }, + { name: 'pricePerPod', internalType: 'uint24', type: 'uint24' }, + { + name: 'maxHarvestableIndex', + internalType: 'uint256', + type: 'uint256', + }, + { + name: 'minFillAmount', + internalType: 'uint256', + type: 'uint256', + }, + { + name: 'mode', + internalType: 'enum LibTransfer.To', + type: 'uint8', + }, + ], + }, + { name: 'beanAmount', internalType: 'uint256', type: 'uint256' }, + { + name: 'mode', + internalType: 'enum LibTransfer.From', + type: 'uint8', + }, + ], + }, + ], + name: 'batchFillPodListing', + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + inputs: [ + { + name: 'params', + internalType: 'struct CreatePodOrderParams[]', + type: 'tuple[]', + components: [ + { + name: 'podOrder', + internalType: 'struct Order.PodOrder', + type: 'tuple', + components: [ + { name: 'orderer', internalType: 'address', type: 'address' }, + { name: 'fieldId', internalType: 'uint256', type: 'uint256' }, + { name: 'pricePerPod', internalType: 'uint24', type: 'uint24' }, + { + name: 'maxPlaceInLine', + internalType: 'uint256', + type: 'uint256', + }, + { + name: 'minFillAmount', + internalType: 'uint256', + type: 'uint256', + }, + ], + }, + { name: 'beanAmount', internalType: 'uint256', type: 'uint256' }, + { + name: 'mode', + internalType: 'enum LibTransfer.From', + type: 'uint8', + }, + ], + }, + ], + name: 'batchCreatePodOrder', + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + inputs: [ + { + name: 'params', + internalType: 'struct FillPodOrderParams[]', + type: 'tuple[]', + components: [ + { + name: 'podOrder', + internalType: 'struct Order.PodOrder', + type: 'tuple', + components: [ + { name: 'orderer', internalType: 'address', type: 'address' }, + { name: 'fieldId', internalType: 'uint256', type: 'uint256' }, + { name: 'pricePerPod', internalType: 'uint24', type: 'uint24' }, + { + name: 'maxPlaceInLine', + internalType: 'uint256', + type: 'uint256', + }, + { + name: 'minFillAmount', + internalType: 'uint256', + type: 'uint256', + }, + ], + }, + { name: 'index', internalType: 'uint256', type: 'uint256' }, + { name: 'start', internalType: 'uint256', type: 'uint256' }, + { name: 'amount', internalType: 'uint256', type: 'uint256' }, + { name: 'mode', internalType: 'enum LibTransfer.To', type: 'uint8' }, + ], + }, + ], + name: 'batchFillPodOrder', + outputs: [], + stateMutability: 'payable', + }, + { + type: 'function', + inputs: [ + { + name: 'converts', + internalType: 'struct ConvertBatchFacet.ConvertParams[]', + type: 'tuple[]', + components: [ + { name: 'convertData', internalType: 'bytes', type: 'bytes' }, + { name: 'stems', internalType: 'int96[]', type: 'int96[]' }, + { name: 'amounts', internalType: 'uint256[]', type: 'uint256[]' }, + { + name: 'grownStalkSlippage', + internalType: 'int256', + type: 'int256', + }, + ], + }, + ], + name: 'batchConvert', + outputs: [ + { + name: 'convertOutputs', + internalType: 'struct ConvertBatchFacet.ConvertOutput[]', + type: 'tuple[]', + components: [ + { name: 'convertKind', internalType: 'uint8', type: 'uint8' }, + { name: 'toStem', internalType: 'int96', type: 'int96' }, + { name: 'fromAmount', internalType: 'uint256', type: 'uint256' }, + { name: 'toAmount', internalType: 'uint256', type: 'uint256' }, + { name: 'fromBdv', internalType: 'uint256', type: 'uint256' }, + { name: 'toBdv', internalType: 'uint256', type: 'uint256' }, + ], + }, + ], + stateMutability: 'payable', + }, ] as const /** @@ -14637,6 +14869,118 @@ export const useWriteBeanstalk_SowWithReferral = functionName: 'sowWithReferral', }) +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCancelPodListing"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchCancelPodListing = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCancelPodListing', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCancelPodOrder"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchCancelPodOrder = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCancelPodOrder', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCreatePodListing"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchCreatePodListing = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCreatePodListing', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchFillPodListing"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchFillPodListing = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchFillPodListing', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCreatePodOrder"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchCreatePodOrder = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCreatePodOrder', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchFillPodOrder"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchFillPodOrder = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchFillPodOrder', + }) + +/** + * Wraps __{@link useWriteContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchConvert"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useWriteBeanstalk_BatchConvert = + /*#__PURE__*/ createUseWriteContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchConvert', + }) + /** * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ * @@ -15893,6 +16237,118 @@ export const useSimulateBeanstalk_SowWithReferral = functionName: 'sowWithReferral', }) +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCancelPodListing"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchCancelPodListing = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCancelPodListing', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCancelPodOrder"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchCancelPodOrder = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCancelPodOrder', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCreatePodListing"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchCreatePodListing = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCreatePodListing', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchFillPodListing"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchFillPodListing = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchFillPodListing', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchCreatePodOrder"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchCreatePodOrder = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchCreatePodOrder', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchFillPodOrder"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchFillPodOrder = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchFillPodOrder', + }) + +/** + * Wraps __{@link useSimulateContract}__ with `abi` set to __{@link beanstalkAbi}__ and `functionName` set to `"batchConvert"` + * + * - [__View Contract on Ethereum Etherscan__](https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5) + * - + * - [__View Contract on Base Basescan__](https://basescan.org/address/0xD1A0D188E861ed9d15773a2F3574a2e94134bA8f) + * - + * - [__View Contract on Arbitrum One Arbiscan__](https://arbiscan.io/address/0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70) + */ +export const useSimulateBeanstalk_BatchConvert = + /*#__PURE__*/ createUseSimulateContract({ + abi: beanstalkAbi, + address: beanstalkAddress, + functionName: 'batchConvert', + }) + /** * Wraps __{@link useWatchContractEvent}__ with `abi` set to __{@link beanstalkAbi}__ * diff --git a/src/generated/gql/pintostalk/gql.ts b/src/generated/gql/pintostalk/gql.ts index 508c651f8..e63e17bf9 100644 --- a/src/generated/gql/pintostalk/gql.ts +++ b/src/generated/gql/pintostalk/gql.ts @@ -26,6 +26,8 @@ type 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}": 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, + "query MyPodListings($first: Int = 100, $skip: Int = 0, $account: String!, $maxHarvestableIndex: BigInt!) {\n podListings(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: createdAt\n orderDirection: desc\n ) {\n ...PodListing\n }\n}": typeof types.MyPodListingsDocument, + "query MyPodOrders($first: Int = 100, $skip: Int = 0, $account: String!) {\n podOrders(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE}\n orderBy: createdAt\n orderDirection: desc\n ) {\n ...PodOrder\n }\n}": typeof types.MyPodOrdersDocument, "fragment PodFill on PodFill {\n id\n placeInLine\n amount\n index\n start\n costInBeans\n fromFarmer {\n id\n }\n toFarmer {\n id\n }\n listing {\n id\n originalAmount\n }\n order {\n id\n beanAmount\n }\n createdAt\n}": typeof types.PodFillFragmentDoc, "fragment PodListing on PodListing {\n id\n farmer {\n id\n }\n historyID\n index\n start\n mode\n pricingType\n pricePerPod\n pricingFunction\n maxHarvestableIndex\n minFillAmount\n originalIndex\n originalPlaceInLine\n originalAmount\n filled\n amount\n remainingAmount\n filledAmount\n fill {\n placeInLine\n }\n status\n createdAt\n updatedAt\n creationHash\n}": typeof types.PodListingFragmentDoc, "fragment PodOrder on PodOrder {\n id\n farmer {\n id\n }\n historyID\n pricingType\n pricePerPod\n pricingFunction\n maxPlaceInLine\n minFillAmount\n beanAmount\n podAmountFilled\n beanAmountFilled\n status\n createdAt\n updatedAt\n creationHash\n}": typeof types.PodOrderFragmentDoc, @@ -54,6 +56,8 @@ const documents: 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}": 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, + "query MyPodListings($first: Int = 100, $skip: Int = 0, $account: String!, $maxHarvestableIndex: BigInt!) {\n podListings(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: createdAt\n orderDirection: desc\n ) {\n ...PodListing\n }\n}": types.MyPodListingsDocument, + "query MyPodOrders($first: Int = 100, $skip: Int = 0, $account: String!) {\n podOrders(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE}\n orderBy: createdAt\n orderDirection: desc\n ) {\n ...PodOrder\n }\n}": types.MyPodOrdersDocument, "fragment PodFill on PodFill {\n id\n placeInLine\n amount\n index\n start\n costInBeans\n fromFarmer {\n id\n }\n toFarmer {\n id\n }\n listing {\n id\n originalAmount\n }\n order {\n id\n beanAmount\n }\n createdAt\n}": types.PodFillFragmentDoc, "fragment PodListing on PodListing {\n id\n farmer {\n id\n }\n historyID\n index\n start\n mode\n pricingType\n pricePerPod\n pricingFunction\n maxHarvestableIndex\n minFillAmount\n originalIndex\n originalPlaceInLine\n originalAmount\n filled\n amount\n remainingAmount\n filledAmount\n fill {\n placeInLine\n }\n status\n createdAt\n updatedAt\n creationHash\n}": types.PodListingFragmentDoc, "fragment PodOrder on PodOrder {\n id\n farmer {\n id\n }\n historyID\n pricingType\n pricePerPod\n pricingFunction\n maxPlaceInLine\n minFillAmount\n beanAmount\n podAmountFilled\n beanAmountFilled\n status\n createdAt\n updatedAt\n creationHash\n}": types.PodOrderFragmentDoc, @@ -132,6 +136,14 @@ export function graphql(source: "query AllPodOrders($first: Int = 1000, $status: * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "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 documents)["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}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "query MyPodListings($first: Int = 100, $skip: Int = 0, $account: String!, $maxHarvestableIndex: BigInt!) {\n podListings(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: createdAt\n orderDirection: desc\n ) {\n ...PodListing\n }\n}"): (typeof documents)["query MyPodListings($first: Int = 100, $skip: Int = 0, $account: String!, $maxHarvestableIndex: BigInt!) {\n podListings(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE, maxHarvestableIndex_gt: $maxHarvestableIndex, remainingAmount_gt: \"100000\"}\n orderBy: createdAt\n orderDirection: desc\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 MyPodOrders($first: Int = 100, $skip: Int = 0, $account: String!) {\n podOrders(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE}\n orderBy: createdAt\n orderDirection: desc\n ) {\n ...PodOrder\n }\n}"): (typeof documents)["query MyPodOrders($first: Int = 100, $skip: Int = 0, $account: String!) {\n podOrders(\n first: $first\n skip: $skip\n where: {farmer: $account, status: ACTIVE}\n orderBy: createdAt\n orderDirection: desc\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 78aa7d75a..9b4ded273 100644 --- a/src/generated/gql/pintostalk/graphql.ts +++ b/src/generated/gql/pintostalk/graphql.ts @@ -14130,6 +14130,25 @@ export type FarmerMarketActivityQueryVariables = Exact<{ export type FarmerMarketActivityQuery = { __typename?: 'Query', podListings: Array<{ __typename?: 'PodListing', id: string, historyID: string, index: any, start: any, mode: number, pricingType?: number | null, pricePerPod: number, pricingFunction?: any | null, maxHarvestableIndex: any, minFillAmount: any, originalIndex: any, originalPlaceInLine: any, originalAmount: any, filled: any, amount: any, remainingAmount: any, filledAmount: any, status: MarketStatus, createdAt: any, updatedAt: any, creationHash: any, farmer: { __typename?: 'Farmer', id: any }, fill?: { __typename?: 'PodFill', placeInLine: any } | null }>, podOrders: Array<{ __typename?: 'PodOrder', id: string, historyID: string, pricingType?: number | null, pricePerPod: number, pricingFunction?: any | null, maxPlaceInLine: any, minFillAmount: any, beanAmount: any, podAmountFilled: any, beanAmountFilled: any, status: MarketStatus, createdAt: any, updatedAt: any, creationHash: any, farmer: { __typename?: 'Farmer', id: any } }>, podFills: Array<{ __typename?: 'PodFill', id: string, placeInLine: any, amount: any, index: any, start: any, costInBeans: any, createdAt: any, fromFarmer: { __typename?: 'Farmer', id: any }, toFarmer: { __typename?: 'Farmer', id: any }, listing?: { __typename?: 'PodListing', id: string, originalAmount: any } | null, order?: { __typename?: 'PodOrder', id: string, beanAmount: any } | null }> }; +export type MyPodListingsQueryVariables = Exact<{ + first?: InputMaybe; + skip?: InputMaybe; + account: Scalars['String']['input']; + maxHarvestableIndex: Scalars['BigInt']['input']; +}>; + + +export type MyPodListingsQuery = { __typename?: 'Query', podListings: Array<{ __typename?: 'PodListing', id: string, historyID: string, index: any, start: any, mode: number, pricingType?: number | null, pricePerPod: number, pricingFunction?: any | null, maxHarvestableIndex: any, minFillAmount: any, originalIndex: any, originalPlaceInLine: any, originalAmount: any, filled: any, amount: any, remainingAmount: any, filledAmount: any, status: MarketStatus, createdAt: any, updatedAt: any, creationHash: any, farmer: { __typename?: 'Farmer', id: any }, fill?: { __typename?: 'PodFill', placeInLine: any } | null }> }; + +export type MyPodOrdersQueryVariables = Exact<{ + first?: InputMaybe; + skip?: InputMaybe; + account: Scalars['String']['input']; +}>; + + +export type MyPodOrdersQuery = { __typename?: 'Query', podOrders: Array<{ __typename?: 'PodOrder', id: string, historyID: string, pricingType?: number | null, pricePerPod: number, pricingFunction?: any | null, maxPlaceInLine: any, minFillAmount: any, beanAmount: any, podAmountFilled: any, beanAmountFilled: any, status: MarketStatus, createdAt: any, updatedAt: any, creationHash: any, farmer: { __typename?: 'Farmer', id: any } }> }; + export type PodFillFragment = { __typename?: 'PodFill', id: string, placeInLine: any, amount: any, index: any, start: any, costInBeans: any, createdAt: any, fromFarmer: { __typename?: 'Farmer', id: any }, toFarmer: { __typename?: 'Farmer', id: any }, listing?: { __typename?: 'PodListing', id: string, originalAmount: any } | null, order?: { __typename?: 'PodOrder', id: string, beanAmount: any } | null }; export type PodListingFragment = { __typename?: 'PodListing', id: string, historyID: string, index: any, start: any, mode: number, pricingType?: number | null, pricePerPod: number, pricingFunction?: any | null, maxHarvestableIndex: any, minFillAmount: any, originalIndex: any, originalPlaceInLine: any, originalAmount: any, filled: any, amount: any, remainingAmount: any, filledAmount: any, status: MarketStatus, createdAt: any, updatedAt: any, creationHash: any, farmer: { __typename?: 'Farmer', id: any }, fill?: { __typename?: 'PodFill', placeInLine: any } | null }; @@ -14243,6 +14262,8 @@ export const AllMarketActivityDocument = {"kind":"Document","definitions":[{"kin export const AllPodListingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllPodListings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"1000"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"status"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MarketStatus"}},"defaultValue":{"kind":"EnumValue","value":"ACTIVE"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"maxHarvestableIndex"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skip"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"podListings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"skip"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skip"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"status"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"maxHarvestableIndex_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"maxHarvestableIndex"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"remainingAmount_gt"},"value":{"kind":"StringValue","value":"100000","block":false}}]}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"index"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"asc"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodListing"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodListing"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodListing"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"start"}},{"kind":"Field","name":{"kind":"Name","value":"mode"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxHarvestableIndex"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"originalIndex"}},{"kind":"Field","name":{"kind":"Name","value":"originalPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"originalAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filled"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"remainingAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filledAmount"}},{"kind":"Field","name":{"kind":"Name","value":"fill"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"placeInLine"}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}}]} as unknown as DocumentNode; export const AllPodOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllPodOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"1000"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"status"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"MarketStatus"}},"defaultValue":{"kind":"EnumValue","value":"ACTIVE"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"skip"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"podOrders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"skip"},"value":{"kind":"Variable","name":{"kind":"Name","value":"skip"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"createdAt"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"status"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodOrder"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodOrder"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodOrder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmount"}},{"kind":"Field","name":{"kind":"Name","value":"podAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}}]} as unknown as DocumentNode; export const FarmerMarketActivityDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FarmerMarketActivity"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"1000"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"account"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"listings_createdAt_gt"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orders_createdAt_gt"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"BigInt"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"podListings"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"farmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"listings_createdAt_gt"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"status_not"},"value":{"kind":"EnumValue","value":"FILLED_PARTIAL"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodListing"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podOrders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"createdAt"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"farmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orders_createdAt_gt"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodOrder"}}]}},{"kind":"Field","name":{"kind":"Name","value":"podFills"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"createdAt_gt"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fill_createdAt_gt"}}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"fromFarmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"toFarmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}}]}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PodFill"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodListing"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodListing"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"start"}},{"kind":"Field","name":{"kind":"Name","value":"mode"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxHarvestableIndex"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"originalIndex"}},{"kind":"Field","name":{"kind":"Name","value":"originalPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"originalAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filled"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"remainingAmount"}},{"kind":"Field","name":{"kind":"Name","value":"filledAmount"}},{"kind":"Field","name":{"kind":"Name","value":"fill"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"placeInLine"}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodOrder"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodOrder"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"farmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"historyID"}},{"kind":"Field","name":{"kind":"Name","value":"pricingType"}},{"kind":"Field","name":{"kind":"Name","value":"pricePerPod"}},{"kind":"Field","name":{"kind":"Name","value":"pricingFunction"}},{"kind":"Field","name":{"kind":"Name","value":"maxPlaceInLine"}},{"kind":"Field","name":{"kind":"Name","value":"minFillAmount"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmount"}},{"kind":"Field","name":{"kind":"Name","value":"podAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmountFilled"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"creationHash"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PodFill"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PodFill"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"placeInLine"}},{"kind":"Field","name":{"kind":"Name","value":"amount"}},{"kind":"Field","name":{"kind":"Name","value":"index"}},{"kind":"Field","name":{"kind":"Name","value":"start"}},{"kind":"Field","name":{"kind":"Name","value":"costInBeans"}},{"kind":"Field","name":{"kind":"Name","value":"fromFarmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"toFarmer"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"listing"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"originalAmount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"order"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"beanAmount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; +export const MyPodListingsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MyPodListings"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"100"}},{"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":"account"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"maxHarvestableIndex"}},"type":{"kind":"NonNullType","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":"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":"farmer"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"status"},"value":{"kind":"EnumValue","value":"ACTIVE"}},{"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":"createdAt"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}}],"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 MyPodOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MyPodOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}},"defaultValue":{"kind":"IntValue","value":"100"}},{"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":"account"}},"type":{"kind":"NonNullType","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":"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":"status"},"value":{"kind":"EnumValue","value":"ACTIVE"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"createdAt"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"desc"}}],"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 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; export const FarmerSeasonalSiloDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FarmerSeasonalSilo"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"from"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"to"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"account"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"siloHourlySnapshots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"silo"},"value":{"kind":"Variable","name":{"kind":"Name","value":"account"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_gte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"from"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"season_lte"},"value":{"kind":"Variable","name":{"kind":"Name","value":"to"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"EnumValue","value":"season"}},{"kind":"Argument","name":{"kind":"Name","value":"orderDirection"},"value":{"kind":"EnumValue","value":"asc"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"plantedBeans"}},{"kind":"Field","name":{"kind":"Name","value":"stalk"}},{"kind":"Field","name":{"kind":"Name","value":"germinatingStalk"}},{"kind":"Field","name":{"kind":"Name","value":"depositedBDV"}}]}}]}}]} as unknown as DocumentNode; diff --git a/src/lib/claim/depositUtils.ts b/src/lib/claim/depositUtils.ts index 923812fdf..0130b505c 100644 --- a/src/lib/claim/depositUtils.ts +++ b/src/lib/claim/depositUtils.ts @@ -3,7 +3,7 @@ import { DepositGroup } from "@/components/CombineSelect"; import { siloHelpersABI } from "@/constants/abi/SiloHelpersABI"; import { tractorHelpersABI } from "@/constants/abi/TractorHelpersABI"; import { SILO_HELPERS_ADDRESS, TRACTOR_HELPERS_ADDRESS } from "@/constants/address"; -import convert from "@/encoders/silo/convert"; +import batchConvert, { type BatchConvertParams } from "@/encoders/batchConvert"; import { beanstalkAbi } from "@/generated/contractHooks"; import { calculateConvertData } from "@/utils/convert"; import { Token, TokenDepositData } from "@/utils/types"; @@ -133,7 +133,8 @@ export function generateCombineAndL2LCallData(farmerDeposits: Map token.isWhitelisted) - .flatMap(([token, depositData]) => encodeClaimRewardCombineCalls(depositData.deposits, token)); + .map(([token, depositData]) => encodeClaimRewardCombineCalls(depositData.deposits, token)) + .filter((call): call is `0x${string}` => call !== undefined); } /** @@ -547,44 +548,63 @@ export function createSmartGroups(deposits: DepositData[], targetGroups: number return newGroups; } +/** + * Encodes deposit combine operations for manual combining using batchConvert. + * Groups deposits and encodes all groups as a single batchConvert call. + * + * @param validGroups Array of deposit groups to combine + * @param token The token to combine deposits for + * @param deposits Array of all farmer deposits for the token + * @returns Single encoded batchConvert calldata, or undefined if no groups need combining + * + * @see encodeClaimRewardCombineCalls for automatic claim-time combining + */ export function encodeGroupCombineCalls( validGroups: DepositGroup[], token: Token, deposits: DepositData[], -): `0x${string}`[] { +): `0x${string}` | undefined { // Exclude groups with only one deposit, since they don't need combining const groupsToEncode = validGroups.filter((group) => group.deposits.length > 1); - return groupsToEncode.map((group) => { - // Get selected deposits for this group + if (groupsToEncode.length === 0) return undefined; + + // Build BatchConvertParams for ALL groups + const batchParams: BatchConvertParams[] = groupsToEncode.map((group) => { const selectedDepositData = group.deposits .map((stem) => deposits.find((d) => d.stem.toHuman() === stem)) - .filter(Boolean); + .filter((d): d is DepositData => d !== undefined); const totalAmount = selectedDepositData.reduce((sum, deposit) => { - if (!deposit) return sum; return deposit.amount.add(sum); }, TokenValue.ZERO); const convertData = calculateConvertData(token, token, totalAmount, totalAmount); if (!convertData) throw new Error("Failed to prepare combine data"); - const stems = selectedDepositData.filter((d): d is DepositData => d !== undefined).map((d) => d.stem.toBigInt()); - const amounts = selectedDepositData - .filter((d): d is DepositData => d !== undefined) - .map((d) => d.amount.toBigInt()); + const stems = selectedDepositData.map((d) => d.stem.toBigInt()); + const amounts = selectedDepositData.map((d) => d.amount.toBigInt()); - // Use the imported convert function instead - return convert(convertData, stems, amounts).callData; + return { convertData, stems, amounts, grownStalkSlippage: 0n }; }); + + // Return SINGLE batchConvert call with all groups + return batchConvert(batchParams); } -// Add a new function to handle claim reward grouping and encoding +/** + * Encodes deposit combine operations for a single token using batchConvert. + * Groups deposits by stalk/BDV ratio and encodes all groups as a single batchConvert call. + * @param deposits Array of deposit data for the token + * @param token The token to combine deposits for + * @param targetGroups Number of groups to create (default 20) + * @returns Single encoded batchConvert calldata, or undefined if no groups need combining + */ export function encodeClaimRewardCombineCalls( deposits: DepositData[], token: Token, targetGroups: number = 20, -): `0x${string}`[] { +): `0x${string}` | undefined { // Use our existing smart grouping logic const groups = createSmartGroups(deposits, targetGroups); @@ -598,7 +618,6 @@ export function encodeClaimRewardCombineCalls( const totalBdv = groupDeposits.reduce((sum, deposit) => sum.add(deposit.depositBdv), TokenValue.ZERO); - // Calculate the stalk-to-BDV ratio for the entire group const stalkPerBdv = totalBdv.gt(0) ? totalStalk.div(totalBdv) : TokenValue.ZERO; return { @@ -610,14 +629,30 @@ export function encodeClaimRewardCombineCalls( // Sort by Stalk/BDV ratio (highest to lowest) const sortedGroups = groupsWithRatio .sort((a, b) => b.stalkPerBdv.sub(a.stalkPerBdv).toNumber()) - .map(({ id, deposits, stalkPerBdv }) => ({ id, deposits, stalkPerBdv })); + .map(({ id, deposits }) => ({ id, deposits })); - // Use our existing encode function with sorted groups - strip stalkPerBdv before passing - const result = encodeGroupCombineCalls( - sortedGroups.map(({ id, deposits }) => ({ id, deposits })), - token, - deposits, - ); + // Build BatchConvertParams for each group with >1 deposit + const batchParams: BatchConvertParams[] = sortedGroups + .filter((group) => group.deposits.length > 1) + .map((group) => { + const selectedDepositData = group.deposits + .map((stem) => deposits.find((d) => d.stem.toHuman() === stem)) + .filter((d): d is DepositData => d !== undefined); + + const totalAmount = selectedDepositData.reduce((sum, deposit) => { + return deposit.amount.add(sum); + }, TokenValue.ZERO); + + const convertData = calculateConvertData(token, token, totalAmount, totalAmount); + if (!convertData) throw new Error("Failed to prepare combine data"); + + const stems = selectedDepositData.map((d) => d.stem.toBigInt()); + const amounts = selectedDepositData.map((d) => d.amount.toBigInt()); + + return { convertData, stems, amounts, grownStalkSlippage: 0n }; + }); + + if (batchParams.length === 0) return undefined; - return result; + return batchConvert(batchParams); } diff --git a/src/pages/Market.tsx b/src/pages/Market.tsx index 229afc2c6..dc259adf2 100644 --- a/src/pages/Market.tsx +++ b/src/pages/Market.tsx @@ -22,9 +22,12 @@ 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 { useAccount } from "wagmi"; import { AllActivityTable } from "./market/AllActivityTable"; import { FarmerActivityTable } from "./market/FarmerActivityTable"; import MarketModeSelect from "./market/MarketModeSelect"; +import { MyListingsTable } from "./market/MyListingsTable"; +import { MyOrdersTable } from "./market/MyOrdersTable"; import { PodListingsTable } from "./market/PodListingsTable"; import { PodOrdersTable } from "./market/PodOrdersTable"; import CreateListing, { PodListingData } from "./market/actions/CreateListing"; @@ -33,8 +36,8 @@ import FillListing from "./market/actions/FillListing"; import FillOrder from "./market/actions/FillOrder"; // Constants -const TABLE_SLUGS = ["activity", "listings", "orders", "my-activity"]; -const TABLE_LABELS = ["Activity", "Listings", "Orders", "My Activity"]; +const TABLE_SLUGS = ["activity", "listings", "orders", "my-activity", "my-listings", "my-orders"]; +const TABLE_LABELS = ["Activity", "Listings", "Orders", "My Activity", "My Listings", "My Orders"]; const SELECTED_PLOT_PURPLE_COLOR = "#8B5CF6"; const SELECTED_PLOT_BORDER_WIDTH = 1.75; @@ -220,6 +223,9 @@ export function Market() { const chartXMax = Math.round((podLineAsNumber / 10) * 10); const harvestableIndex = useHarvestableIndex(); const navHeight = useNavHeight(); + const account = useAccount(); + const [selectedListingIds, setSelectedListingIds] = useState([]); + const [selectedOrderIds, setSelectedOrderIds] = useState([]); const [mounted, setMounted] = useState(false); const [selectedPlotData, setSelectedPlotData] = useState<{ @@ -1040,7 +1046,7 @@ export function Market() { {TABLE_SLUGS.map((s, idx) => (

{TABLE_LABELS[idx]} @@ -1053,6 +1059,20 @@ export function Market() { {tab === TABLE_SLUGS[1] && } {tab === TABLE_SLUGS[2] && } {tab === TABLE_SLUGS[3] && } + {tab === TABLE_SLUGS[4] && ( + + )} + {tab === TABLE_SLUGS[5] && ( + + )}

void; +} + +export function MyListingsTable({ address, selectedIds = [], onSelectionChange }: MyListingsTableProps) { + const BEAN = useTokenData().mainToken; + const harvestableIndex = useHarvestableIndex(); + + // Fetch user's listings via GraphQL hook + const { listings, isLoading } = useMyPodListings({ account: address }); + + const rowsPerPage = 12; + const totalRows = listings?.length || 0; + const totalPages = Math.ceil(totalRows / rowsPerPage); + const [currentPage, setCurrentPage] = useState(1); + const newestEventOnPage = rowsPerPage * currentPage - rowsPerPage; + const oldestEventOnPage = rowsPerPage * currentPage - 1; + + // Selection is enabled when onSelectionChange callback is provided + const selectable = !!onSelectionChange; + + // Immediate cancellation state + const [cancellingIds, setCancellingIds] = useState>(new Set()); + + const diamondAddress = useProtocolAddress(); + const queryClient = useQueryClient(); + const { allPodListings, allMarket, farmerMarket } = useQueryKeys({ + account: address, + harvestableIndex, + }); + + const onCancelSuccess = useCallback(() => { + queryClient.invalidateQueries({ queryKey: allPodListings }); + queryClient.invalidateQueries({ queryKey: allMarket }); + queryClient.invalidateQueries({ queryKey: farmerMarket }); + if (onSelectionChange) { + onSelectionChange([]); + } + }, [queryClient, allPodListings, allMarket, farmerMarket, onSelectionChange]); + + const { writeWithEstimateGas, submitting } = useTransaction({ + successMessage: "Listing(s) cancelled successfully", + errorMessage: "Failed to cancel listing(s)", + successCallback: onCancelSuccess, + }); + + // Handle immediate single cancel + const handleCancelImmediate = useCallback( + async (listing: MarketplacePodListing) => { + if (!address) { + toast.error("Wallet not connected"); + return; + } + + setCancellingIds((prev) => new Set(prev).add(listing.id)); + + try { + trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_LIST_CANCEL, { + listing_count: 1, + is_batch: false, + }); + + await writeWithEstimateGas({ + address: diamondAddress, + abi: beanstalkAbi, + functionName: "cancelPodListing", + args: [0n, TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt()], + }); + } finally { + setCancellingIds((prev) => { + const next = new Set(prev); + next.delete(listing.id); + return next; + }); + } + }, + [address, diamondAddress, writeWithEstimateGas], + ); + + // Handle immediate bulk cancel + const handleBulkCancelImmediate = useCallback(async () => { + if (!address) { + toast.error("Wallet not connected"); + return; + } + + const listingsToCancel = listings?.filter((l) => selectedIds.includes(l.id)) || []; + const ownedListings = listingsToCancel.filter((l) => l.farmer.id.toLowerCase() === address.toLowerCase()); + + if (ownedListings.length === 0) { + toast.error("No owned listings to cancel"); + return; + } + + try { + trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_LIST_CANCEL, { + listing_count: ownedListings.length, + is_batch: true, + }); + + const batchArgs = ownedListings.map((listing) => ({ + fieldId: 0n, + index: TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), + })); + + await writeWithEstimateGas({ + address: diamondAddress, + abi: beanstalkAbi, + functionName: "batchCancelPodListing", + args: [batchArgs], + }); + } catch (error) { + console.error("Batch cancel failed:", error); + } + }, [address, listings, selectedIds, diamondAddress, writeWithEstimateGas]); + + // Compute visible listings on current page for select all logic + const visibleListings = listings?.slice(newestEventOnPage, oldestEventOnPage + 1) || []; + const visibleIds = visibleListings.map((l) => l.id); + const allVisibleSelected = visibleIds.length > 0 && visibleIds.every((id) => selectedIds.includes(id)); + const someVisibleSelected = visibleIds.some((id) => selectedIds.includes(id)) && !allVisibleSelected; + + // Handle "Select All" checkbox for visible listings on current page + const handleSelectAllChange = useCallback( + (checked: boolean) => { + if (!onSelectionChange) return; + if (checked) { + // Add all visible IDs that aren't already selected + const newSelection = [...selectedIds]; + for (const id of visibleIds) { + if (!newSelection.includes(id)) { + newSelection.push(id); + } + } + onSelectionChange(newSelection); + } else { + // Remove only the visible IDs from selection + onSelectionChange(selectedIds.filter((id) => !visibleIds.includes(id))); + } + }, + [selectedIds, visibleIds, onSelectionChange], + ); + + // Handle checkbox change for a single row + const handleCheckboxChange = useCallback( + (listingId: string, checked: boolean) => { + if (!onSelectionChange) return; + if (checked) { + onSelectionChange([...selectedIds, listingId]); + } else { + onSelectionChange(selectedIds.filter((id) => id !== listingId)); + } + }, + [selectedIds, onSelectionChange], + ); + + return ( + + +
+

Your active Pod Listings

+ {isLoading && } +
+
+ + {selectable && selectedIds.length > 0 && ( +
+ +
+ )} + + <> + + + {selectable && ( + + handleSelectAllChange(checked === true)} + aria-label="Select all visible listings" + /> + + )} + Created At + Amount + Place In Line + Price + Fill % + Expires In + + + + + {(!listings || listings.length === 0) && !isLoading && ( + + You have no active listings + + )} + {listings?.map((listing, i) => { + const dateOptions: Intl.DateTimeFormatOptions = { + year: "2-digit", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + hourCycle: "h24", + }; + const createdAt = new Date(Number(listing.createdAt) * 1000); + const originalAmount = parseFloat(listing.originalAmount); + const remainingAmount = parseFloat(listing.remainingAmount); + const fillPct = (originalAmount - remainingAmount) / originalAmount; + if (i >= newestEventOnPage && i <= oldestEventOnPage) + return ( + + {selectable && ( + + handleCheckboxChange(listing.id, checked === true)} + onClick={(e) => e.stopPropagation()} + aria-label={`Select listing ${listing.id}`} + /> + + )} + {createdAt.toLocaleString(undefined, dateOptions)} + +
+ + {TokenValue.fromBlockchain(listing.amount, PODS.decimals).toHuman("short")} +
+
+ + {TokenValue.fromBlockchain(listing.index, PODS.decimals).sub(harvestableIndex).toHuman("short")} + + +
+ + {TokenValue.fromBlockchain(listing.pricePerPod, BEAN.decimals).toHuman()} +
+
+ {formatter.pct(fillPct * 100)} + + {TokenValue.fromBlockchain(listing.maxHarvestableIndex, PODS.decimals) + .sub(harvestableIndex) + .toHuman("short")}{" "} + PODS + + + + +
+ ); + })} +
+ +
+ {totalPages > 1 && ( + + )} +
+
+ ); +} diff --git a/src/pages/market/MyOrdersTable.tsx b/src/pages/market/MyOrdersTable.tsx new file mode 100644 index 000000000..663124a3f --- /dev/null +++ b/src/pages/market/MyOrdersTable.tsx @@ -0,0 +1,326 @@ +import podIcon from "@/assets/protocol/Pod.png"; +import pintoIcon from "@/assets/tokens/PINTO.png"; +import { TokenValue } from "@/classes/TokenValue"; +import FrameAnimator from "@/components/LoadingSpinner"; +import { MarketPaginationControls } from "@/components/MarketPaginationControls"; +import { Card, CardContent, CardHeader } from "@/components/ui/Card"; +import { Checkbox } from "@/components/ui/Checkbox"; +import IconImage from "@/components/ui/IconImage"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/Table"; +import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; +import { PODS } from "@/constants/internalTokens"; +import { beanstalkAbi } from "@/generated/contractHooks"; +import { MyPodOrdersQuery } from "@/generated/gql/pintostalk/graphql"; +import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; +import useTransaction from "@/hooks/useTransaction"; +import useMyPodOrders from "@/state/market/useMyPodOrders"; +import { useQueryKeys } from "@/state/useQueryKeys"; +import useTokenData from "@/state/useTokenData"; +import { trackSimpleEvent } from "@/utils/analytics"; +import { formatter } from "@/utils/format"; +import { FarmToMode } from "@/utils/types"; +import { useQueryClient } from "@tanstack/react-query"; +import { useCallback, useState } from "react"; +import { toast } from "sonner"; + +export type MarketplacePodOrder = MyPodOrdersQuery["podOrders"][number]; + +export interface MyOrdersTableProps { + /** Connected wallet address */ + address: string | undefined; + /** IDs of selected orders */ + selectedIds?: string[]; + /** Callback when selection changes */ + onSelectionChange?: (selectedIds: string[]) => void; +} + +export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: MyOrdersTableProps) { + const { orders = [], isLoading } = useMyPodOrders({ account: address }); + const BEAN = useTokenData().mainToken; + + const rowsPerPage = 12; + const totalRows = orders?.length || 0; + const totalPages = Math.ceil(totalRows / rowsPerPage); + const [currentPage, setCurrentPage] = useState(1); + const newestEventOnPage = rowsPerPage * currentPage - rowsPerPage; + const oldestEventOnPage = rowsPerPage * currentPage - 1; + + // Selection is enabled when onSelectionChange callback is provided + const selectable = !!onSelectionChange; + + // Immediate cancellation state + const [cancellingIds, setCancellingIds] = useState>(new Set()); + + const diamondAddress = useProtocolAddress(); + const queryClient = useQueryClient(); + const { allPodOrders, allMarket, farmerMarket } = useQueryKeys({ + account: address, + }); + + const onCancelSuccess = useCallback(() => { + queryClient.invalidateQueries({ queryKey: allPodOrders }); + queryClient.invalidateQueries({ queryKey: allMarket }); + queryClient.invalidateQueries({ queryKey: farmerMarket }); + if (onSelectionChange) { + onSelectionChange([]); + } + }, [queryClient, allPodOrders, allMarket, farmerMarket, onSelectionChange]); + + const { writeWithEstimateGas, submitting } = useTransaction({ + successMessage: "Order(s) cancelled successfully", + errorMessage: "Failed to cancel order(s)", + successCallback: onCancelSuccess, + }); + + // Handle immediate single cancel + const handleCancelImmediate = useCallback( + async (order: MarketplacePodOrder) => { + if (!address) { + toast.error("Wallet not connected"); + return; + } + + setCancellingIds((prev) => new Set(prev).add(order.id)); + + try { + trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_ORDER_CANCEL, { + order_count: 1, + is_batch: false, + destination_mode: "wallet", + }); + + const maxPlaceInLine = TokenValue.fromBlockchain(order.maxPlaceInLine.toString(), PODS.decimals); + const pricePerPod = TokenValue.fromBlockchain(order.pricePerPod.toString(), BEAN.decimals); + const minFillAmount = TokenValue.fromBlockchain(order.minFillAmount.toString(), PODS.decimals); + + await writeWithEstimateGas({ + address: diamondAddress, + abi: beanstalkAbi, + functionName: "cancelPodOrder", + args: [ + { + orderer: address, + fieldId: 0n, + pricePerPod, + maxPlaceInLine, + minFillAmount, + }, + Number(FarmToMode.EXTERNAL), + ], + }); + } finally { + setCancellingIds((prev) => { + const next = new Set(prev); + next.delete(order.id); + return next; + }); + } + }, + [address, BEAN.decimals, diamondAddress, writeWithEstimateGas], + ); + + // Handle immediate bulk cancel + const handleBulkCancelImmediate = useCallback(async () => { + if (!address) { + toast.error("Wallet not connected"); + return; + } + + const ordersToCancel = orders?.filter((o) => selectedIds.includes(o.id)) || []; + const ownedOrders = ordersToCancel.filter((o) => o.farmer.id.toLowerCase() === address.toLowerCase()); + + if (ownedOrders.length === 0) { + toast.error("No owned orders to cancel"); + return; + } + + try { + trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_ORDER_CANCEL, { + order_count: ownedOrders.length, + is_batch: true, + destination_mode: "wallet", + }); + + const batchArgs = ownedOrders.map((order) => ({ + orderer: address, + fieldId: 0n, + pricePerPod: TokenValue.fromBlockchain(order.pricePerPod.toString(), BEAN.decimals), + maxPlaceInLine: TokenValue.fromBlockchain(order.maxPlaceInLine.toString(), PODS.decimals), + minFillAmount: TokenValue.fromBlockchain(order.minFillAmount.toString(), PODS.decimals), + })); + + await writeWithEstimateGas({ + address: diamondAddress, + abi: beanstalkAbi, + functionName: "batchCancelPodOrder", + args: [batchArgs, Number(FarmToMode.EXTERNAL)], + }); + } catch (error) { + console.error("Batch cancel failed:", error); + } + }, [address, orders, selectedIds, BEAN.decimals, diamondAddress, writeWithEstimateGas]); + + // Compute visible orders on current page for select all logic + const visibleOrders = orders?.slice(newestEventOnPage, oldestEventOnPage + 1) || []; + const visibleIds = visibleOrders.map((o) => o.id); + const allVisibleSelected = visibleIds.length > 0 && visibleIds.every((id) => selectedIds.includes(id)); + const someVisibleSelected = visibleIds.some((id) => selectedIds.includes(id)) && !allVisibleSelected; + + // Handle "Select All" checkbox for visible orders on current page + const handleSelectAllChange = useCallback( + (checked: boolean) => { + if (!onSelectionChange) return; + if (checked) { + const newSelection = [...selectedIds]; + for (const id of visibleIds) { + if (!newSelection.includes(id)) { + newSelection.push(id); + } + } + onSelectionChange(newSelection); + } else { + onSelectionChange(selectedIds.filter((id) => !visibleIds.includes(id))); + } + }, + [selectedIds, visibleIds, onSelectionChange], + ); + + // Handle checkbox change for a single row + const handleCheckboxChange = useCallback( + (orderId: string, checked: boolean) => { + if (!onSelectionChange) return; + if (checked) { + onSelectionChange([...selectedIds, orderId]); + } else { + onSelectionChange(selectedIds.filter((id) => id !== orderId)); + } + }, + [selectedIds, onSelectionChange], + ); + + return ( + + +
+

Your active Pod Orders

+ {isLoading && } +
+
+ + {selectable && selectedIds.length > 0 && ( +
+ +
+ )} + + <> + + + {selectable && ( + + handleSelectAllChange(checked === true)} + aria-label="Select all visible orders" + /> + + )} + Created At + Amount + Place In Line + Price + Fill % + + + + + {(!orders || orders.length === 0) && !isLoading && ( + + You have no active orders + + )} + {orders?.map((order, i) => { + const dateOptions: Intl.DateTimeFormatOptions = { + year: "2-digit", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + hourCycle: "h24", + }; + const createdAt = new Date(Number(order.createdAt) * 1000); + const amount = TokenValue.fromBlockchain(order.beanAmount, BEAN.decimals); + const amountFilled = TokenValue.fromBlockchain(order.beanAmountFilled, BEAN.decimals); + const pricePerPod = TokenValue.fromBlockchain(order.pricePerPod, BEAN.decimals); + const remainingPods = amount.sub(amountFilled).div(pricePerPod); + const fillPct = amountFilled.div(amount).mul(100); + if (i >= newestEventOnPage && i <= oldestEventOnPage) + return ( + + {selectable && ( + + handleCheckboxChange(order.id, checked === true)} + onClick={(e) => e.stopPropagation()} + aria-label={`Select order ${order.id}`} + /> + + )} + {createdAt.toLocaleString(undefined, dateOptions)} + +
+ + {remainingPods.toHuman("short")} +
+
+ + 0 - {TokenValue.fromBlockchain(order.maxPlaceInLine, PODS.decimals).toHuman("short")} + + +
+ + {formatter.number(pricePerPod.toNumber(), { minDecimals: 2, maxDecimals: 2 })} +
+
+ {`${fillPct?.toHuman("short")}%`} + + + +
+ ); + })} +
+ +
+ {totalPages > 1 && ( + + )} +
+
+ ); +} diff --git a/src/pages/market/PodListingsTable.tsx b/src/pages/market/PodListingsTable.tsx index 35d750e2f..345550108 100644 --- a/src/pages/market/PodListingsTable.tsx +++ b/src/pages/market/PodListingsTable.tsx @@ -3,7 +3,7 @@ import pintoIcon from "@/assets/tokens/PINTO.png"; import { TokenValue } from "@/classes/TokenValue"; import FrameAnimator from "@/components/LoadingSpinner"; import { MarketPaginationControls } from "@/components/MarketPaginationControls"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/Card"; +import { Card, CardContent, CardHeader } from "@/components/ui/Card"; import IconImage from "@/components/ui/IconImage"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/Table"; import { PODS } from "@/constants/internalTokens"; @@ -84,7 +84,6 @@ export function PodListingsTable() { day: "2-digit", hour: "2-digit", minute: "2-digit", - // second: "2-digit", hourCycle: "h24", }; const createdAt = new Date(Number(listing.createdAt) * 1000); diff --git a/src/pages/market/actions/CancelListing.tsx b/src/pages/market/actions/CancelListing.tsx index 032308f97..f4003b49e 100644 --- a/src/pages/market/actions/CancelListing.tsx +++ b/src/pages/market/actions/CancelListing.tsx @@ -1,5 +1,6 @@ import { TokenValue } from "@/classes/TokenValue"; import SmartSubmitButton from "@/components/SmartSubmitButton"; +import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import { PODS } from "@/constants/internalTokens"; import { beanstalkAbi } from "@/generated/contractHooks"; import { AllPodListingsQuery } from "@/generated/gql/pintostalk/graphql"; @@ -7,17 +8,23 @@ import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import useTransaction from "@/hooks/useTransaction"; import { useHarvestableIndex } from "@/state/useFieldData"; import { useQueryKeys } from "@/state/useQueryKeys"; +import { trackSimpleEvent } from "@/utils/analytics"; import { useQueryClient } from "@tanstack/react-query"; import { useCallback, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; import { useAccount } from "wagmi"; +export type MarketplacePodListing = AllPodListingsQuery["podListings"][number]; + export interface CancelListingProps { - listing: AllPodListingsQuery["podListings"][number]; + /** Array of listings available for cancellation */ + listings: MarketplacePodListing[]; + /** Optional array of listing IDs to cancel. If not provided, cancels all listings */ + selectedListingIds?: string[]; } -export default function CancelListing({ listing }: CancelListingProps) { +export default function CancelListing({ listings, selectedListingIds }: CancelListingProps) { const diamondAddress = useProtocolAddress(); const account = useAccount(); const harvestableIndex = useHarvestableIndex(); @@ -30,49 +37,86 @@ export default function CancelListing({ listing }: CancelListingProps) { }); const allQK = useMemo(() => [allPodListings, allMarket, farmerMarket], [allPodListings, allMarket, farmerMarket]); + // Filter listings by selectedListingIds if provided, otherwise use all listings + const listingsToCancel = useMemo(() => { + if (selectedListingIds && selectedListingIds.length > 0) { + return listings.filter((listing) => selectedListingIds.includes(listing.id)); + } + return listings; + }, [listings, selectedListingIds]); + + const cancelCount = listingsToCancel.length; + const isBatch = cancelCount > 1; + const onSuccess = useCallback(() => { navigate(`/market/pods/sell`); allQK.forEach((key) => queryClient.invalidateQueries({ queryKey: key })); }, [navigate, queryClient, allQK]); const { writeWithEstimateGas, submitting, isConfirming, setSubmitting } = useTransaction({ - successMessage: "Cancel Listing successful", - errorMessage: "Cancel Listing failed", + successMessage: isBatch ? `Cancelled ${cancelCount} Listings` : "Cancel Listing successful", + errorMessage: isBatch ? "Batch Cancel Listings failed" : "Cancel Listing failed", successCallback: onSuccess, }); const onSubmit = useCallback(() => { + if (cancelCount === 0) return; + try { setSubmitting(true); - toast.loading("Cancelling Listing..."); + toast.loading(isBatch ? `Cancelling ${cancelCount} Listings...` : "Cancelling Listing..."); + + trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_LIST_CANCEL, { + listing_count: cancelCount, + is_batch: isBatch, + }); + + // Single listing: use original cancelPodListing function + if (!isBatch) { + const listing = listingsToCancel[0]; + return writeWithEstimateGas({ + address: diamondAddress, + abi: beanstalkAbi, + functionName: "cancelPodListing", + args: [ + 0n, // fieldId + TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), // index + ], + }); + } + + // Multiple listings: use batchCancelPodListing + const batchArgs = listingsToCancel.map((listing) => ({ + fieldId: 0n, + index: TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), + })); + return writeWithEstimateGas({ address: diamondAddress, abi: beanstalkAbi, - functionName: "cancelPodListing", - args: [ - 0n, // fieldId - TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), // index - ], + functionName: "batchCancelPodListing", + args: [batchArgs], }); } catch (e) { console.error(e); toast.dismiss(); - toast.error("Cancel Listing Failed"); + toast.error(isBatch ? "Batch Cancel Listings Failed" : "Cancel Listing Failed"); throw e; } finally { setSubmitting(false); } - }, [listing, diamondAddress, setSubmitting, writeWithEstimateGas]); + }, [cancelCount, isBatch, listingsToCancel, diamondAddress, setSubmitting, writeWithEstimateGas]); + + const buttonText = + cancelCount === 0 ? "No Listings to Cancel" : cancelCount > 1 ? `Cancel ${cancelCount} Listings` : "Cancel Listing"; return ( - <> - - + ); } diff --git a/src/pages/market/actions/CancelOrder.tsx b/src/pages/market/actions/CancelOrder.tsx index 65f80fd86..fa9bfbc99 100644 --- a/src/pages/market/actions/CancelOrder.tsx +++ b/src/pages/market/actions/CancelOrder.tsx @@ -3,28 +3,33 @@ import { TV, TokenValue } from "@/classes/TokenValue"; import FarmBalanceToggle from "@/components/FarmBalanceToggle"; import SmartSubmitButton from "@/components/SmartSubmitButton"; import { Separator } from "@/components/ui/Separator"; +import { ANALYTICS_EVENTS } from "@/constants/analytics-events"; import { PODS } from "@/constants/internalTokens"; import { beanstalkAbi } from "@/generated/contractHooks"; -import { AllPodOrdersQuery } from "@/generated/gql/pintostalk/graphql"; +import { MyPodOrdersQuery } from "@/generated/gql/pintostalk/graphql"; import { useProtocolAddress } from "@/hooks/pinto/useProtocolAddress"; import { useFarmTogglePreference } from "@/hooks/useFarmTogglePreference"; import useTransaction from "@/hooks/useTransaction"; import { useFarmerBalances } from "@/state/useFarmerBalances"; import { useQueryKeys } from "@/state/useQueryKeys"; import useTokenData from "@/state/useTokenData"; +import { trackSimpleEvent } from "@/utils/analytics"; import { formatter } from "@/utils/format"; import { FarmToMode } from "@/utils/types"; import { useQueryClient } from "@tanstack/react-query"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; import { useAccount } from "wagmi"; +export type MarketplacePodOrder = MyPodOrdersQuery["podOrders"][number]; + export interface CancelOrderProps { - order: AllPodOrdersQuery["podOrders"][number]; + /** Array of orders available for cancellation */ + orders: MarketplacePodOrder[]; } -export default function CancelOrder({ order }: CancelOrderProps) { +export default function CancelOrder({ orders }: CancelOrderProps) { const mainToken = useTokenData().mainToken; const diamondAddress = useProtocolAddress(); const { queryKeys: balanceQKs } = useFarmerBalances(); @@ -42,52 +47,94 @@ export default function CancelOrder({ order }: CancelOrderProps) { [allPodOrders, allMarket, farmerMarket, balanceQKs], ); + const cancelCount = orders.length; + const isBatch = cancelCount > 1; + const onSuccess = useCallback(() => { navigate(`/market/pods/sell`); allQK.forEach((key) => queryClient.invalidateQueries({ queryKey: key })); }, [navigate, queryClient, allQK]); const { writeWithEstimateGas, submitting, isConfirming, setSubmitting } = useTransaction({ - successMessage: "Cancel Order successful", - errorMessage: "Cancel Order failed", + successMessage: isBatch ? `Cancelled ${cancelCount} Orders` : "Cancel Order successful", + errorMessage: isBatch ? "Batch Cancel Orders failed" : "Cancel Order failed", successCallback: onSuccess, }); - const amountOrder = TokenValue.fromBlockchain(order?.beanAmount || 0, mainToken.decimals); - const amountFilled = TokenValue.fromBlockchain(order?.beanAmountFilled || 0, mainToken.decimals); - const remainingBeans = amountOrder.sub(amountFilled); + // Calculate aggregate remaining beans across all orders + const remainingBeans = useMemo(() => { + let total = TokenValue.fromBlockchain(0, mainToken.decimals); + for (const order of orders) { + const amountOrder = TokenValue.fromBlockchain(order?.beanAmount || 0, mainToken.decimals); + const amountFilled = TokenValue.fromBlockchain(order?.beanAmountFilled || 0, mainToken.decimals); + total = total.add(amountOrder.sub(amountFilled)); + } + return total; + }, [orders, mainToken.decimals]); const onSubmit = useCallback(() => { - const maxPlaceInLine = TokenValue.fromBlockchain(order.maxPlaceInLine.toString(), PODS.decimals); - const pricePerPod = TokenValue.fromBlockchain(order.pricePerPod.toString(), mainToken.decimals); - const minFillAmount = TokenValue.fromBlockchain(order.minFillAmount.toString(), PODS.decimals); + if (cancelCount === 0) return; + try { setSubmitting(true); - toast.loading("Cancelling Order..."); + toast.loading(isBatch ? `Cancelling ${cancelCount} Orders...` : "Cancelling Order..."); + + trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_ORDER_CANCEL, { + order_count: cancelCount, + is_batch: isBatch, + destination_mode: mode === FarmToMode.INTERNAL ? "farm" : "wallet", + }); + + // Single order: use original cancelPodOrder function + if (!isBatch) { + const order = orders[0]; + const maxPlaceInLine = TokenValue.fromBlockchain(order.maxPlaceInLine.toString(), PODS.decimals); + const pricePerPod = TokenValue.fromBlockchain(order.pricePerPod.toString(), mainToken.decimals); + const minFillAmount = TokenValue.fromBlockchain(order.minFillAmount.toString(), PODS.decimals); + return writeWithEstimateGas({ + address: diamondAddress, + abi: beanstalkAbi, + functionName: "cancelPodOrder", + args: [ + { + orderer: account.address, + fieldId: 0n, + pricePerPod, + maxPlaceInLine, + minFillAmount, + }, + Number(mode), + ], + }); + } + + // Multiple orders: use batchCancelPodOrder + const batchArgs = orders.map((order) => ({ + orderer: account.address, + fieldId: 0n, + pricePerPod: TokenValue.fromBlockchain(order.pricePerPod.toString(), mainToken.decimals), + maxPlaceInLine: TokenValue.fromBlockchain(order.maxPlaceInLine.toString(), PODS.decimals), + minFillAmount: TokenValue.fromBlockchain(order.minFillAmount.toString(), PODS.decimals), + })); + return writeWithEstimateGas({ address: diamondAddress, abi: beanstalkAbi, - functionName: "cancelPodOrder", - args: [ - { - orderer: account.address, // account - fieldId: 0n, // fieldId - pricePerPod, // pricePerPod - maxPlaceInLine, // maxPlaceInLine - minFillAmount, // minFillAmount - }, - Number(mode), // mode - ], + functionName: "batchCancelPodOrder", + args: [batchArgs, Number(mode)], }); } catch (e) { console.error(e); toast.dismiss(); - toast.error("Cancel Order Failed"); + toast.error(isBatch ? "Batch Cancel Orders Failed" : "Cancel Order Failed"); throw e; } finally { setSubmitting(false); } - }, [order, diamondAddress, account, toFarm, mainToken, setSubmitting, writeWithEstimateGas]); + }, [cancelCount, isBatch, orders, diamondAddress, account, mode, mainToken, setSubmitting, writeWithEstimateGas]); + + const buttonText = + cancelCount === 0 ? "No Orders to Cancel" : cancelCount > 1 ? `Cancel ${cancelCount} Orders` : "Cancel Order"; return ( <> @@ -101,9 +148,9 @@ export default function CancelOrder({ order }: CancelOrderProps) { ); diff --git a/src/pages/market/actions/CreateListing.tsx b/src/pages/market/actions/CreateListing.tsx index 5cb237f8f..ee4694e80 100644 --- a/src/pages/market/actions/CreateListing.tsx +++ b/src/pages/market/actions/CreateListing.tsx @@ -29,7 +29,6 @@ import { motion } from "framer-motion"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { toast } from "sonner"; -import { encodeFunctionData } from "viem"; import { useAccount } from "wagmi"; interface LocationState { @@ -543,36 +542,25 @@ export default function CreateListing({ onSelectionChange }: CreateListingProps setSubmitting(true); toast.loading(`Creating ${listingData.length} Listing${listingData.length > 1 ? "s" : ""}...`); - const farmData: `0x${string}`[] = []; - - // Create a listing call for each plot - for (const data of listingData) { - const listingArgs = { - lister: account, - fieldId: 0n, - index: data.index.toBigInt(), - start: data.start.toBigInt(), - podAmount: data.amount.toBigInt(), - pricePerPod: encodedPricePerPod, - maxHarvestableIndex: maxHarvestableIndex.toBigInt(), - minFillAmount: minFill.toBigInt(), - mode: Number(balanceTo), - }; - - const listingCall = encodeFunctionData({ - abi: beanstalkAbi, - functionName: "createPodListing", - args: [listingArgs], - }); - farmData.push(listingCall); - } - - // Use farm to batch all listings in one transaction + // Build batch listing params array + const batchListingArgs = listingData.map((data) => ({ + lister: account, + fieldId: 0n, + index: data.index.toBigInt(), + start: data.start.toBigInt(), + podAmount: data.amount.toBigInt(), + pricePerPod: encodedPricePerPod, + maxHarvestableIndex: maxHarvestableIndex.toBigInt(), + minFillAmount: minFill.toBigInt(), + mode: Number(balanceTo), + })); + + // Use native batchCreatePodListing for gas-efficient batch creation writeWithEstimateGas({ address: diamondAddress, abi: beanstalkAbi, - functionName: "farm", - args: [farmData], + functionName: "batchCreatePodListing", + args: [batchListingArgs], }); } catch (e: unknown) { console.error(e); diff --git a/src/pages/market/actions/FillListing.tsx b/src/pages/market/actions/FillListing.tsx index b00a9239f..d4e308beb 100644 --- a/src/pages/market/actions/FillListing.tsx +++ b/src/pages/market/actions/FillListing.tsx @@ -45,7 +45,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { toast } from "sonner"; -import { Address, encodeFunctionData } from "viem"; +import { Address } from "viem"; import { useAccount } from "wagmi"; // Configuration constants @@ -560,47 +560,37 @@ export default function FillListing({ selectedListingId, selectedPlaceInLine, on toast.loading(`Filling ${listingsToFill.length} Listing${listingsToFill.length !== 1 ? "s" : ""}...`); if (isUsingMain) { - // Direct fill - create farm calls for each listing - const farmData: `0x${string}`[] = []; - - for (const { listing, beanAmount } of listingsToFill) { - // Encode pricePerPod with 6 decimals (like CreateOrder.tsx) + // Direct fill - use batchFillPodListing for gas-efficient batching + const batchFillArgs = listingsToFill.map(({ listing, beanAmount }) => { const pricePerPodNumber = TokenValue.fromBlockchain(listing.pricePerPod, mainToken.decimals).toNumber(); const encodedPricePerPod = Math.floor(pricePerPodNumber * PRICE_PER_POD_CONFIG.DECIMAL_MULTIPLIER); - const fillCall = encodeFunctionData({ - abi: beanstalkAbi, - functionName: "fillPodListing", - args: [ - { - lister: listing.farmer.id as Address, - fieldId: 0n, - index: TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), - start: TokenValue.fromBlockchain(listing.start, PODS.decimals).toBigInt(), - podAmount: TokenValue.fromBlockchain(listing.amount, PODS.decimals).toBigInt(), - pricePerPod: encodedPricePerPod, - maxHarvestableIndex: TokenValue.fromBlockchain(listing.maxHarvestableIndex, PODS.decimals).toBigInt(), - minFillAmount: TokenValue.fromBlockchain(listing.minFillAmount, mainToken.decimals).toBigInt(), - mode: Number(listing.mode), - }, - beanAmount.toBigInt(), - Number(balanceFrom), - ], - }); - - farmData.push(fillCall); - } + return { + podListing: { + lister: listing.farmer.id as Address, + fieldId: 0n, + index: TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt(), + start: TokenValue.fromBlockchain(listing.start, PODS.decimals).toBigInt(), + podAmount: TokenValue.fromBlockchain(listing.amount, PODS.decimals).toBigInt(), + pricePerPod: encodedPricePerPod, + maxHarvestableIndex: TokenValue.fromBlockchain(listing.maxHarvestableIndex, PODS.decimals).toBigInt(), + minFillAmount: TokenValue.fromBlockchain(listing.minFillAmount, mainToken.decimals).toBigInt(), + mode: Number(listing.mode), + }, + beanAmount: beanAmount.toBigInt(), + mode: Number(balanceFrom), + }; + }); - if (farmData.length === 0) { + if (batchFillArgs.length === 0) { throw new Error("No valid fill operations to execute"); } - // Use farm to batch all listing fills in one transaction return writeWithEstimateGas({ address: diamondAddress, abi: beanstalkAbi, - functionName: "farm", - args: [farmData], + functionName: "batchFillPodListing", + args: [batchFillArgs], }); } else if (swapBuild?.advancedFarm.length) { // Swap + fill - use advancedFarm diff --git a/src/pages/market/actions/FillOrder.tsx b/src/pages/market/actions/FillOrder.tsx index a75aa2af9..e515605f9 100644 --- a/src/pages/market/actions/FillOrder.tsx +++ b/src/pages/market/actions/FillOrder.tsx @@ -27,7 +27,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { toast } from "sonner"; -import { Address, encodeFunctionData } from "viem"; +import { Address } from "viem"; import { useAccount } from "wagmi"; import CancelOrder from "./CancelOrder"; @@ -336,7 +336,19 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { let currentPlot = sortedPlots[0]; let currentPlotStartOffset = 0; - const farmData: `0x${string}`[] = []; + const batchFillArgs: { + podOrder: { + orderer: Address; + fieldId: bigint; + pricePerPod: number; + maxPlaceInLine: bigint; + minFillAmount: bigint; + }; + index: bigint; + start: bigint; + amount: bigint; + mode: number; + }[] = []; for (const { order: orderToFill, amount: fillAmount } of ordersToFill) { let remainingAmount = fillAmount; @@ -370,29 +382,20 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { const podAmount = TokenValue.fromHuman(podsToUse, PODS.decimals); const startOffset = TokenValue.fromHuman(currentPlotStartOffset, PODS.decimals); - // Create fillPodOrder call for this order with pod allocation from current plot - const fillOrderArgs = { - orderer: orderToFill.farmer.id as Address, - fieldId: FIELD_ID, - maxPlaceInLine: BigInt(orderToFill.maxPlaceInLine), - pricePerPod: Number(orderToFill.pricePerPod), - minFillAmount: BigInt(orderToFill.minFillAmount), - }; - - const fillCall = encodeFunctionData({ - abi: beanstalkAbi, - functionName: "fillPodOrder", - args: [ - fillOrderArgs, - currentPlot.index.toBigInt(), - startOffset.toBigInt(), - podAmount.toBigInt(), - Number(FarmToMode.INTERNAL), - ], + batchFillArgs.push({ + podOrder: { + orderer: orderToFill.farmer.id as Address, + fieldId: FIELD_ID, + pricePerPod: Number(orderToFill.pricePerPod), + maxPlaceInLine: BigInt(orderToFill.maxPlaceInLine), + minFillAmount: BigInt(orderToFill.minFillAmount), + }, + index: currentPlot.index.toBigInt(), + start: startOffset.toBigInt(), + amount: podAmount.toBigInt(), + mode: Number(FarmToMode.INTERNAL), }); - farmData.push(fillCall); - // Update tracking variables remainingAmount -= podsToUse; remainingPodsInCurrentPlot -= podsToUse; @@ -407,17 +410,17 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { } } - if (farmData.length === 0) { + if (batchFillArgs.length === 0) { throw new Error("No valid fill operations to execute"); } - // Use farm to batch all order fills in one transaction + // Use batchFillPodOrder for gas-efficient batching // Success state will be set in onSuccess callback via ref writeWithEstimateGas({ address: diamondAddress, abi: beanstalkAbi, - functionName: "farm", - args: [farmData], + functionName: "batchFillPodOrder", + args: [batchFillArgs], }); } catch (e) { console.error("Fill order error:", e); @@ -509,11 +512,7 @@ export default function FillOrder({ selectedOrderId }: FillOrderProps) { {isOwnOrder && selectedOrders.length > 0 && ( <> - {selectedOrders - .filter((order) => order.farmer.id === account.address?.toLowerCase()) - .map((order) => ( - - ))} + order.farmer.id === account.address?.toLowerCase())} /> )} diff --git a/src/queries/beanstalk/podmarket/MyPodListings.graphql b/src/queries/beanstalk/podmarket/MyPodListings.graphql new file mode 100644 index 000000000..1777f23ee --- /dev/null +++ b/src/queries/beanstalk/podmarket/MyPodListings.graphql @@ -0,0 +1,23 @@ +#import "./PodListing.fragment.graphql" + +query MyPodListings( + $first: Int = 100 + $skip: Int = 0 + $account: String! + $maxHarvestableIndex: BigInt! +) { + podListings( + first: $first + skip: $skip + where: { + farmer: $account + status: ACTIVE + maxHarvestableIndex_gt: $maxHarvestableIndex + remainingAmount_gt: "100000" # = 0.10 Pods. hides dust pods. + } + orderBy: createdAt + orderDirection: desc + ) { + ...PodListing + } +} diff --git a/src/queries/beanstalk/podmarket/MyPodOrders.graphql b/src/queries/beanstalk/podmarket/MyPodOrders.graphql new file mode 100644 index 000000000..236567b71 --- /dev/null +++ b/src/queries/beanstalk/podmarket/MyPodOrders.graphql @@ -0,0 +1,20 @@ +#import "./PodOrder.fragment.graphql" + +query MyPodOrders( + $first: Int = 100 + $skip: Int = 0 + $account: String! +) { + podOrders( + first: $first + skip: $skip + where: { + farmer: $account + status: ACTIVE + } + orderBy: createdAt + orderDirection: desc + ) { + ...PodOrder + } +} diff --git a/src/state/market/useMyPodListings.ts b/src/state/market/useMyPodListings.ts new file mode 100644 index 000000000..d3a26a631 --- /dev/null +++ b/src/state/market/useMyPodListings.ts @@ -0,0 +1,47 @@ +import { subgraphs } from "@/constants/subgraph"; +import { MyPodListingsDocument } 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 { useHarvestableIndex } from "../useFieldData"; + +export interface UseMyPodListingsProps { + /** Connected wallet address to filter listings by */ + account: string | undefined; +} + +/** + * Fetches pod listings owned by the specified account. + * Returns only active, non-expired listings with remaining amount > 0.10 pods. + */ +export default function useMyPodListings({ account }: UseMyPodListingsProps) { + const chainId = useChainId(); + const harvestableIndex = useHarvestableIndex(); + + const queryKey = useMemo( + () => ["myPodListings", { chainId, account: account?.toLowerCase(), harvestableIndex: harvestableIndex.toHuman() }], + [chainId, account, harvestableIndex], + ); + + const query = useQuery({ + queryKey, + queryFn: async () => + request(subgraphs[chainId].beanstalk, MyPodListingsDocument, { + account: account?.toLowerCase(), + maxHarvestableIndex: harvestableIndex.toBigInt().toString(), + first: 100, + skip: 0, + }), + enabled: !!account && harvestableIndex.gt(0), + }); + + return { + data: query.data, + listings: query.data?.podListings, + isLoading: query.isLoading, + isFetching: query.isFetching, + isLoaded: !!query.data, + queryKey, + }; +} diff --git a/src/state/market/useMyPodOrders.ts b/src/state/market/useMyPodOrders.ts new file mode 100644 index 000000000..1335928a8 --- /dev/null +++ b/src/state/market/useMyPodOrders.ts @@ -0,0 +1,41 @@ +import { subgraphs } from "@/constants/subgraph"; +import { MyPodOrdersDocument } from "@/generated/gql/pintostalk/graphql"; +import { useQuery } from "@tanstack/react-query"; +import request from "graphql-request"; +import { useMemo } from "react"; +import { useChainId } from "wagmi"; + +export interface UseMyPodOrdersProps { + /** Connected wallet address to filter orders by */ + account: string | undefined; +} + +/** + * Fetches pod orders owned by the specified account. + * Returns only active orders. + */ +export default function useMyPodOrders({ account }: UseMyPodOrdersProps) { + const chainId = useChainId(); + + const queryKey = useMemo(() => ["myPodOrders", { chainId, account: account?.toLowerCase() }], [chainId, account]); + + const query = useQuery({ + queryKey, + queryFn: async () => + request(subgraphs[chainId].beanstalk, MyPodOrdersDocument, { + account: account?.toLowerCase(), + first: 100, + skip: 0, + }), + enabled: !!account, + }); + + return { + data: query.data, + orders: query.data?.podOrders, + isLoading: query.isLoading, + isFetching: query.isFetching, + isLoaded: !!query.data, + queryKey, + }; +} From ee15fe265f0a331d9179a7edab592c00877bca0a Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:49:52 +0000 Subject: [PATCH 2/4] refactor: simplify pricePerPod conversion in batchCancelPodOrder Use toNumber() method directly instead of Number(toBigInt()) Co-authored-by: fr1jo --- src/encoders/batchCancelPodOrder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/encoders/batchCancelPodOrder.ts b/src/encoders/batchCancelPodOrder.ts index 8cae47f16..f76d5280d 100644 --- a/src/encoders/batchCancelPodOrder.ts +++ b/src/encoders/batchCancelPodOrder.ts @@ -21,7 +21,7 @@ export default function batchCancelPodOrder(orders: CancelPodOrderParams[], mode const args = orders.map((o) => ({ orderer: o.orderer, fieldId: o.fieldId, - pricePerPod: Number(o.pricePerPod.toBigInt()), // uint24 expects number, not bigint + pricePerPod: o.pricePerPod.toNumber(), // uint24 expects number, not bigint maxPlaceInLine: o.maxPlaceInLine.toBigInt(), minFillAmount: o.minFillAmount.toBigInt(), })); From fc0bc3d704f68853e2df427cc40006828953af4a Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 15:58:52 +0000 Subject: [PATCH 3/4] fix(market): use setSubmitting in MyListingsTable submit handlers Properly manage button state during transaction submission to prevent multiple clicks by using setSubmitting from useTransaction hook. Co-authored-by: fr1jo --- src/pages/market/MyListingsTable.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/market/MyListingsTable.tsx b/src/pages/market/MyListingsTable.tsx index f0d9cc52e..d1c22fa9f 100644 --- a/src/pages/market/MyListingsTable.tsx +++ b/src/pages/market/MyListingsTable.tsx @@ -68,7 +68,7 @@ export function MyListingsTable({ address, selectedIds = [], onSelectionChange } } }, [queryClient, allPodListings, allMarket, farmerMarket, onSelectionChange]); - const { writeWithEstimateGas, submitting } = useTransaction({ + const { writeWithEstimateGas, submitting, setSubmitting } = useTransaction({ successMessage: "Listing(s) cancelled successfully", errorMessage: "Failed to cancel listing(s)", successCallback: onCancelSuccess, @@ -82,6 +82,7 @@ export function MyListingsTable({ address, selectedIds = [], onSelectionChange } return; } + setSubmitting(true); setCancellingIds((prev) => new Set(prev).add(listing.id)); try { @@ -97,6 +98,7 @@ export function MyListingsTable({ address, selectedIds = [], onSelectionChange } args: [0n, TokenValue.fromBlockchain(listing.index, PODS.decimals).toBigInt()], }); } finally { + setSubmitting(false); setCancellingIds((prev) => { const next = new Set(prev); next.delete(listing.id); @@ -104,7 +106,7 @@ export function MyListingsTable({ address, selectedIds = [], onSelectionChange } }); } }, - [address, diamondAddress, writeWithEstimateGas], + [address, diamondAddress, writeWithEstimateGas, setSubmitting], ); // Handle immediate bulk cancel @@ -122,6 +124,8 @@ export function MyListingsTable({ address, selectedIds = [], onSelectionChange } return; } + setSubmitting(true); + try { trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_LIST_CANCEL, { listing_count: ownedListings.length, @@ -141,8 +145,10 @@ export function MyListingsTable({ address, selectedIds = [], onSelectionChange } }); } catch (error) { console.error("Batch cancel failed:", error); + } finally { + setSubmitting(false); } - }, [address, listings, selectedIds, diamondAddress, writeWithEstimateGas]); + }, [address, listings, selectedIds, diamondAddress, writeWithEstimateGas, setSubmitting]); // Compute visible listings on current page for select all logic const visibleListings = listings?.slice(newestEventOnPage, oldestEventOnPage + 1) || []; From ad3a946a380da902a0f29655d63b3e75627a2cfb Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 16:49:27 +0000 Subject: [PATCH 4/4] fix(market): use setSubmitting in MyOrdersTable submit handlers Properly manage button state during transaction submission by: - Destructuring setSubmitting from useTransaction - Calling setSubmitting(true) at start of submit handlers - Calling setSubmitting(false) in finally blocks - Updating dependency arrays to include setSubmitting This prevents buttons from being clickable during transaction processing. Co-authored-by: fr1jo --- src/pages/market/MyOrdersTable.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/market/MyOrdersTable.tsx b/src/pages/market/MyOrdersTable.tsx index 663124a3f..0f710d6d8 100644 --- a/src/pages/market/MyOrdersTable.tsx +++ b/src/pages/market/MyOrdersTable.tsx @@ -66,7 +66,7 @@ export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: } }, [queryClient, allPodOrders, allMarket, farmerMarket, onSelectionChange]); - const { writeWithEstimateGas, submitting } = useTransaction({ + const { writeWithEstimateGas, submitting, setSubmitting } = useTransaction({ successMessage: "Order(s) cancelled successfully", errorMessage: "Failed to cancel order(s)", successCallback: onCancelSuccess, @@ -80,6 +80,7 @@ export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: return; } + setSubmitting(true); setCancellingIds((prev) => new Set(prev).add(order.id)); try { @@ -109,6 +110,7 @@ export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: ], }); } finally { + setSubmitting(false); setCancellingIds((prev) => { const next = new Set(prev); next.delete(order.id); @@ -116,7 +118,7 @@ export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: }); } }, - [address, BEAN.decimals, diamondAddress, writeWithEstimateGas], + [address, BEAN.decimals, diamondAddress, writeWithEstimateGas, setSubmitting], ); // Handle immediate bulk cancel @@ -134,6 +136,8 @@ export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: return; } + setSubmitting(true); + try { trackSimpleEvent(ANALYTICS_EVENTS.MARKET.POD_ORDER_CANCEL, { order_count: ownedOrders.length, @@ -157,8 +161,10 @@ export function MyOrdersTable({ address, selectedIds = [], onSelectionChange }: }); } catch (error) { console.error("Batch cancel failed:", error); + } finally { + setSubmitting(false); } - }, [address, orders, selectedIds, BEAN.decimals, diamondAddress, writeWithEstimateGas]); + }, [address, orders, selectedIds, BEAN.decimals, diamondAddress, writeWithEstimateGas, setSubmitting]); // Compute visible orders on current page for select all logic const visibleOrders = orders?.slice(newestEventOnPage, oldestEventOnPage + 1) || [];