Skip to content
Open
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
6 changes: 6 additions & 0 deletions packages/social-controllers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Add optional `tokenImageUrl` field to `Position` type and `PositionStruct` validation schema ([#8448](https://github.com/MetaMask/core/pull/8448))
- Add optional `avgHoldMinutes` field to `TraderStats` type and `TraderStatsStruct` validation schema ([#8448](https://github.com/MetaMask/core/pull/8448))
- Add `intent` and optional `category` fields to `Trade` type ([#8410](https://github.com/MetaMask/core/pull/8410))
- Export `TradeStruct` superstruct schema; derive `Trade` type via `Infer` ([#8410](https://github.com/MetaMask/core/pull/8410))
- Narrow `direction` to `'buy' | 'sell'` and `intent` to `'enter' | 'exit'` on `Trade` type ([#8410](https://github.com/MetaMask/core/pull/8410))

### Fixed

- Fix `fetchClosedPositions` using v2 URL instead of v1, which caused 404 errors since the closed positions endpoint only exists on v1 ([#8448](https://github.com/MetaMask/core/pull/8448))

### Changed

- Bump `@metamask/messenger` from `^1.1.0` to `^1.1.1` ([#8373](https://github.com/MetaMask/core/pull/8373))
Expand Down
4 changes: 3 additions & 1 deletion packages/social-controllers/src/SocialService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const mockPosition = {
costBasis: 3000,
trades: [mockTrade],
lastTradeAt: 1700000000,
tokenImageUrl: 'https://assets.daylight.xyz/images/token-eth.png',
};

function createMessenger(): SocialServiceMessenger {
Expand Down Expand Up @@ -239,6 +240,7 @@ describe('SocialService', () => {
winRate7d: 0.7,
roiPercent7d: 1.2,
tradeCount7d: 15,
avgHoldMinutes: 120,
},
perChainBreakdown: {
perChainPnl: { base: 30000 },
Expand Down Expand Up @@ -477,7 +479,7 @@ describe('SocialService', () => {

expect(result).toStrictEqual(mockPositionsResponse);
expect(mockFetch).toHaveBeenCalledWith(
`${V2_URL}/traders/0x1234/positions/closed`,
`${V1_URL}/traders/0x1234/positions/closed`,
);
});

Expand Down
6 changes: 5 additions & 1 deletion packages/social-controllers/src/SocialService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const PositionStruct = structType({
costBasis: number(),
trades: array(TradeStruct),
lastTradeAt: number(),
tokenImageUrl: optional(nullable(string())),
currentValueUSD: optional(nullable(number())),
pnlValueUsd: optional(nullable(number())),
pnlPercent: optional(nullable(number())),
Expand Down Expand Up @@ -119,6 +120,7 @@ const TraderStatsStruct = structType({
winRate7d: optional(nullable(number())),
roiPercent7d: optional(nullable(number())),
tradeCount7d: optional(nullable(number())),
avgHoldMinutes: optional(nullable(number())),
});

const PerChainBreakdownStruct = structType({
Expand Down Expand Up @@ -546,8 +548,10 @@ export class SocialService extends BaseDataService<
page ?? null,
],
queryFn: async () => {
const baseUrl =
status === 'open' ? this.#v2Url : this.#v1Url;
const url = new URL(
`${this.#v2Url}/traders/${encodeURIComponent(addressOrId)}/positions/${status}`,
`${baseUrl}/traders/${encodeURIComponent(addressOrId)}/positions/${status}`,
);
if (chain) {
url.searchParams.append('chain', chain);
Expand Down
4 changes: 4 additions & 0 deletions packages/social-controllers/src/social-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export type TraderStats = {
winRate7d?: number | null;
roiPercent7d?: number | null;
tradeCount7d?: number | null;
/** Median holding time in minutes. */
avgHoldMinutes?: number | null;
Comment on lines +108 to +109
Copy link
Copy Markdown
Contributor

@Bigshmow Bigshmow Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized our UI designs show this as "avg. hold" but clicker calls this medianHoldingTimeMinutes
See profile response: https://docs.clicker.xyz/api-reference/profile
And the comment acknowledge this discrepancy, so wdyt is it ok to keep as is or is the misrepresentation (median vs mean) not acceptable?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can go with medianHoldMinutes here instead, aligned with product needs the front end will now simply say "hold time" instead of avg. hold.

Suggested change
/** Median holding time in minutes. */
avgHoldMinutes?: number | null;
/** Median holding time in minutes. */
medianHoldMinutes?: number | null;

};

export type PerChainBreakdown = {
Expand Down Expand Up @@ -142,6 +144,8 @@ export type Position = {
costBasis: number;
trades: Trade[];
lastTradeAt: number;
/** Daylight-hosted token image URL. */
tokenImageUrl?: string | null;
/** Current USD value of the remaining position (open positions only). */
currentValueUSD?: number | null;
/** Unrealized + realized PnL in USD. */
Expand Down
Loading