Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/combo-redeem-position-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@polymarket/client": patch
---

Add support for redeeming full combo position balances by position ID.
5 changes: 5 additions & 0 deletions .changeset/combo-split-merge-legs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@polymarket/client": patch
---

Add support for splitting and merging combo positions by legs, including `amount: 'max'` for combo merge.
6 changes: 6 additions & 0 deletions .changeset/list-combo-positions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@polymarket/bindings": patch
"@polymarket/client": patch
---

Add `listComboPositions` for fetching combo positions with typed response bindings and SDK-owned pagination.
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
- When translating one public error into another at an action boundary, prefer `ResultAsync.mapErr(...)` on the request pipeline over `try`/`catch` around `unwrap(...)` when the remap can stay inside the result chain.
- When an action starts calling another action, awaiting a workflow step, waiting on a transaction handle, or otherwise adding a new operation that can throw, validate the containing action's public `...Error` union and runtime `makeErrorGuard(...)`. Add any newly propagated public errors unless they are caught, remapped, or intentionally handled before crossing the action boundary.
- Prefer TypeScript enums with `z.enum(MyEnum)` over `z.union([z.literal(...), ...])` for string-valued sets. This gives consumers dot-notation access, keeps the schema and type in sync, and avoids `z.nativeEnum` which is deprecated in Zod v4.
- Document abstractions at their own layer. Lower-level types, helpers, and modules should describe their own contract, invariants, and direct behavior, not higher-level consumers that happen to compose them.
- In TSDoc `@example` blocks, do not include import statements. Keep examples focused on usage only.
- Public TSDoc must not mention underlying service boundaries such as Gamma, CLOB, Data API, or relayer. Public docs should describe the unified SDK surface, while tests may mention the underlying services when useful.
- For any public SDK function export, including actions and client methods, document the public thrown-error surface explicitly. Export a flattened `...Error` union of the concrete public error types the function can throw through its public contract, dedupe the union, and do not include internal assertion-style errors such as `InvariantError` in that union.
Expand Down
162 changes: 162 additions & 0 deletions packages/bindings/src/data/portfolio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@ import {
DecimalishSchema,
EpochSecondsToMillisecondsSchema,
IsoCalendarDateStringSchema,
IsoDateTimeStringSchema,
MixedDateTimeStringSchema,
PaginationCursorSchema,
PositionIdSchema,
TokenIdSchema,
} from '../shared';
import { AddressSchema } from './common';

export enum ComboPositionStatus {
Open = 'OPEN',
Partial = 'PARTIAL',
ResolvedWin = 'RESOLVED_WIN',
ResolvedLoss = 'RESOLVED_LOSS',
}

export const ComboPositionStatusSchema = z.enum(ComboPositionStatus);

export const PositionSchema = z
.object({
proxyWallet: AddressSchema.nullish(),
Expand Down Expand Up @@ -107,6 +119,147 @@ export const MetaMarketPositionSchema = z.object({
positions: z.array(MarketPositionSchema).nullish(),
});

export const ComboPositionMarketEventSchema = z
.object({
event_id: z.string().nullish(),
event_slug: z.string().nullish(),
event_title: z.string().nullish(),
event_image: z.string().nullish(),
})
.transform(({ event_id, event_slug, event_title, event_image }) => ({
eventId: event_id,
eventSlug: event_slug,
eventTitle: event_title,
eventImage: event_image,
}));

export const ComboPositionMarketSchema = z
.object({
market_id: z.string().nullish(),
slug: z.string().nullish(),
title: z.string().nullish(),
outcome: z.string().nullish(),
image_url: z.string().nullish(),
icon_url: z.string().nullish(),
category: z.string().nullish(),
subcategory: z.string().nullish(),
tags: z.array(z.string()).nullish(),
end_date: IsoDateTimeStringSchema.nullish(),
event: ComboPositionMarketEventSchema.nullish(),
})
.transform(({ market_id, image_url, icon_url, end_date, ...rest }) => ({
...rest,
marketId: market_id,
imageUrl: image_url,
iconUrl: icon_url,
endDate: end_date,
}));

export const ComboPositionLegSchema = z
.object({
leg_index: z.number().int(),
leg_position_id: PositionIdSchema,
leg_condition_id: ConditionIdSchema,
leg_outcome_index: z.number().int(),
leg_outcome_label: z.string().nullish(),
leg_status: ComboPositionStatusSchema,
leg_resolved_at: IsoDateTimeStringSchema.nullish(),
leg_current_price: DecimalishSchema.nullish(),
market: ComboPositionMarketSchema.nullish(),
})
.transform(
({
leg_index,
leg_position_id,
leg_condition_id,
leg_outcome_index,
leg_outcome_label,
leg_status,
leg_resolved_at,
leg_current_price,
...rest
}) => ({
...rest,
legIndex: leg_index,
legPositionId: leg_position_id,
legConditionId: leg_condition_id,
legOutcomeIndex: leg_outcome_index,
legOutcomeLabel: leg_outcome_label,
legStatus: leg_status,
legResolvedAt: leg_resolved_at,
legCurrentPrice: leg_current_price,
}),
);

export const ComboPositionSchema = z
.object({
combo_condition_id: ConditionIdSchema,
combo_position_id: PositionIdSchema,
module_id: z.number().int(),
user_address: AddressSchema,
shares_balance: DecimalishSchema,
entry_avg_price_usdc: DecimalishSchema.nullish(),
entry_cost_usdc: DecimalishSchema.nullish(),
status: ComboPositionStatusSchema,
first_entry_at: IsoDateTimeStringSchema,
resolved_at: IsoDateTimeStringSchema.nullish(),
legs_total: z.number().int(),
legs_resolved: z.number().int(),
legs_pending: z.number().int(),
legs: z.array(ComboPositionLegSchema),
})
.transform(
({
combo_condition_id,
combo_position_id,
module_id,
user_address,
shares_balance,
entry_avg_price_usdc,
entry_cost_usdc,
first_entry_at,
resolved_at,
legs_total,
legs_resolved,
legs_pending,
...rest
}) => ({
...rest,
conditionId: combo_condition_id,
positionId: combo_position_id,
moduleId: module_id,
userAddress: user_address,
shares: shares_balance,
entryAvgPriceUsdc: entry_avg_price_usdc,
entryCostUsdc: entry_cost_usdc,
firstEntryAt: first_entry_at,
resolvedAt: resolved_at,
legsTotal: legs_total,
legsResolved: legs_resolved,
legsPending: legs_pending,
}),
);

export const ListComboPositionsResponseSchema = z
.object({
combos: z.array(ComboPositionSchema),
pagination: z.object({
limit: z.number().int(),
offset: z.number().int(),
has_more: z.boolean(),
next_cursor: PaginationCursorSchema.nullish(),
}),
})
.transform(({ pagination, ...rest }) => ({
...rest,
pagination: {
limit: pagination.limit,
offset: pagination.offset,
hasMore: pagination.has_more,
nextCursor: pagination.next_cursor,
},
}));

export const ListPositionsResponseSchema = z.array(PositionSchema);
export const ListClosedPositionsResponseSchema = z.array(ClosedPositionSchema);
export const FetchPortfolioValueResponseSchema = z.array(ValueSchema);
Expand All @@ -119,6 +272,12 @@ export type ClosedPosition = z.infer<typeof ClosedPositionSchema>;
export type Value = z.infer<typeof ValueSchema>;
export type MarketPosition = z.infer<typeof MarketPositionSchema>;
export type MetaMarketPosition = z.infer<typeof MetaMarketPositionSchema>;
export type ComboPositionMarketEvent = z.infer<
typeof ComboPositionMarketEventSchema
>;
export type ComboPositionMarket = z.infer<typeof ComboPositionMarketSchema>;
export type ComboPositionLeg = z.infer<typeof ComboPositionLegSchema>;
export type ComboPosition = z.infer<typeof ComboPositionSchema>;
export type ListPositionsResponse = z.infer<typeof ListPositionsResponseSchema>;
export type ListClosedPositionsResponse = z.infer<
typeof ListClosedPositionsResponseSchema
Expand All @@ -129,3 +288,6 @@ export type FetchPortfolioValueResponse = z.infer<
export type ListMarketPositionsResponse = z.infer<
typeof ListMarketPositionsResponseSchema
>;
export type ListComboPositionsResponse = z.infer<
typeof ListComboPositionsResponseSchema
>;
8 changes: 7 additions & 1 deletion packages/bindings/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ export function toCommentId(value: string): CommentId {
}

export function toConditionId(value: string): ConditionId {
return to32ByteHexString(value) as ConditionId;
if (!isHexString(value) || (value.length !== 64 && value.length !== 66)) {
throw new TypeError(
`Expected a 31-byte or 32-byte hex string, received: ${value}`,
);
}

return value as ConditionId;
}

export function toCollectionId(value: string): CollectionId {
Expand Down
Loading
Loading