Skip to content

Commit e393041

Browse files
committed
Release v11.1.0
Origin-SHA: bbe72b6405aad02845d2b39d2706d893c4b165de
1 parent ed2afd7 commit e393041

7 files changed

Lines changed: 197 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,39 @@
11
# @opensea/sdk
22

3+
## 11.1.0
4+
5+
### Minor Changes
6+
7+
- 8fa9fb5: Expose the new `token/{chain}/{address}/holders` and `token/{chain}/{address}/liquidity-pools` endpoints across SDK, CLI, and skill.
8+
9+
## SDK (`@opensea/sdk`)
10+
11+
- `OpenSeaAPI.getTokenHolders(chain, address, args?)``TokenHoldersResponse` — paginated holders (`limit`, `cursor`, `sortBy: "QUANTITY"`, `sortDirection`) plus aggregate distribution health (`STRONG | HEALTHY | CONCERNING | BAD`).
12+
- `OpenSeaAPI.getTokenLiquidityPools(chain, address, args?)``TokenLiquidityPoolsResponse` — pools with pool type, USD reserves, bonding-curve progress, graduation flag.
13+
- New type exports: `TokenHoldersResponse`, `TokenHoldersArgs`, `TokenLiquidityPoolsResponse`, `TokenLiquidityPoolsArgs`.
14+
- New path helpers in `apiPaths.ts`: `getTokenHoldersPath`, `getTokenLiquidityPoolsPath`.
15+
16+
## CLI (`@opensea/cli`)
17+
18+
- `opensea tokens holders <chain> <address> [--limit] [--next] [--sort-by] [--sort-direction]`
19+
- `opensea tokens liquidity-pools <chain> <address> [--limit]`
20+
- SDK class additions: `OpenSeaCLI.tokens.holders(...)`, `OpenSeaCLI.tokens.liquidityPools(...)`.
21+
- New type re-exports: `TokenHoldersResponse`, `TokenLiquidityPoolsResponse`.
22+
23+
## Skill (`@opensea/skill`)
24+
25+
- `tokens/opensea-token-holders.sh <chain> <address> [limit] [cursor] [sort_by] [sort_direction]`
26+
- `tokens/opensea-token-liquidity-pools.sh <chain> <address> [limit]`
27+
- Documentation: added rows to `SKILL.md` (Investigation Scripts) and `references/rest-api.md` (Tokens).
28+
29+
Bumps consume `@opensea/api-types` 0.4.3 (released alongside, see the spec-sync PR for full schema details).
30+
31+
### Patch Changes
32+
33+
- Updated dependencies [96928f4]
34+
- Updated dependencies [90702a7]
35+
- @opensea/api-types@0.4.3
36+
337
## 11.0.0
438

539
### Major Changes

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opensea/sdk",
3-
"version": "11.0.0",
3+
"version": "11.1.0",
44
"description": "TypeScript SDK for the OpenSea marketplace helps developers build new experiences using NFTs, tokens, and our marketplace data",
55
"license": "MIT",
66
"author": "OpenSea Developers",
@@ -45,7 +45,7 @@
4545
"types": "lib/index.d.ts",
4646
"dependencies": {
4747
"@noble/hashes": "^2.0.1",
48-
"@opensea/api-types": "^0.4.2",
48+
"@opensea/api-types": "^0.4.3",
4949
"@opensea/seaport-js": "^4.1.1",
5050
"ethers": "^6.16.0"
5151
},

src/api/api.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ import {
116116
type SweepCollectionResponse,
117117
type TokenActivityArgs,
118118
type TokenBatchResponse,
119+
type TokenHoldersArgs,
120+
type TokenHoldersResponse,
121+
type TokenLiquidityPoolsArgs,
122+
type TokenLiquidityPoolsResponse,
119123
type TokenSwapActivityPaginatedResponse,
120124
type TokenTimeSeriesArgs,
121125
type TraitFilter,
@@ -1078,6 +1082,30 @@ export class OpenSeaAPI {
10781082
return this.tokensAPI.getTokenActivity(chain, address, args)
10791083
}
10801084

1085+
/**
1086+
* Fetch paginated holders for a token, including quantity held, USD value,
1087+
* and aggregate distribution health (STRONG | HEALTHY | CONCERNING | BAD).
1088+
*/
1089+
public async getTokenHolders(
1090+
chain: Chain,
1091+
address: string,
1092+
args?: TokenHoldersArgs,
1093+
): Promise<TokenHoldersResponse> {
1094+
return this.tokensAPI.getTokenHolders(chain, address, args)
1095+
}
1096+
1097+
/**
1098+
* Fetch liquidity pools for a token (pool type, USD reserves, and
1099+
* bonding-curve progress / graduation flag where applicable).
1100+
*/
1101+
public async getTokenLiquidityPools(
1102+
chain: Chain,
1103+
address: string,
1104+
args?: TokenLiquidityPoolsArgs,
1105+
): Promise<TokenLiquidityPoolsResponse> {
1106+
return this.tokensAPI.getTokenLiquidityPools(chain, address, args)
1107+
}
1108+
10811109
/**
10821110
* Fetch multiple NFTs in a single request.
10831111
*/

src/api/apiPaths.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,14 @@ export const getTokenActivityPath = (chain: Chain, address: string) => {
321321
return `${API_V2_PREFIX}/chain/${chain}/token/${address}/activity`
322322
}
323323

324+
export const getTokenHoldersPath = (chain: Chain, address: string) => {
325+
return `${API_V2_PREFIX}/chain/${chain}/token/${address}/holders`
326+
}
327+
328+
export const getTokenLiquidityPoolsPath = (chain: Chain, address: string) => {
329+
return `${API_V2_PREFIX}/chain/${chain}/token/${address}/liquidity-pools`
330+
}
331+
324332
// ── NFT analytics ───────────────────────────────────────────────────
325333

326334
export const getNFTOwnersPath = (

src/api/tokens.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
getTokenActivityPath,
77
getTokenGroupPath,
88
getTokenGroupsPath,
9+
getTokenHoldersPath,
10+
getTokenLiquidityPoolsPath,
911
getTokenOhlcvPath,
1012
getTokenPath,
1113
getTokenPriceHistoryPath,
@@ -30,6 +32,10 @@ import type {
3032
SwapExecuteResponse,
3133
TokenActivityArgs,
3234
TokenBatchResponse,
35+
TokenHoldersArgs,
36+
TokenHoldersResponse,
37+
TokenLiquidityPoolsArgs,
38+
TokenLiquidityPoolsResponse,
3339
TokenSwapActivityPaginatedResponse,
3440
TokenTimeSeriesArgs,
3541
} from "./types"
@@ -161,4 +167,35 @@ export class TokensAPI {
161167
args,
162168
)
163169
}
170+
171+
/**
172+
* Fetch paginated holders for a token, including quantity held, USD value,
173+
* and an aggregate distribution health label (STRONG | HEALTHY |
174+
* CONCERNING | BAD).
175+
*/
176+
async getTokenHolders(
177+
chain: Chain,
178+
address: string,
179+
args?: TokenHoldersArgs,
180+
): Promise<TokenHoldersResponse> {
181+
return this.fetcher.get<TokenHoldersResponse>(
182+
getTokenHoldersPath(chain, address),
183+
args,
184+
)
185+
}
186+
187+
/**
188+
* Fetch liquidity pools for a token (pool type, reserves in USD, and
189+
* bonding-curve progress / graduation flag where applicable).
190+
*/
191+
async getTokenLiquidityPools(
192+
chain: Chain,
193+
address: string,
194+
args?: TokenLiquidityPoolsArgs,
195+
): Promise<TokenLiquidityPoolsResponse> {
196+
return this.fetcher.get<TokenLiquidityPoolsResponse>(
197+
getTokenLiquidityPoolsPath(chain, address),
198+
args,
199+
)
200+
}
164201
}

src/api/types.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,8 @@ import type {
11931193
ProfileCollectionsResponse as ApiProfileCollectionsResponse,
11941194
SwapTransactionResponse as ApiSwapTransactionResponse,
11951195
TokenBatchResponse as ApiTokenBatchResponse,
1196+
TokenHoldersResponse as ApiTokenHoldersResponse,
1197+
TokenLiquidityPoolsResponse as ApiTokenLiquidityPoolsResponse,
11961198
TokenSwapActivityPaginatedResponse as ApiTokenSwapActivityPaginatedResponse,
11971199
TransferRequest as ApiTransferRequest,
11981200
TransferResponse as ApiTransferResponse,
@@ -1236,6 +1238,9 @@ export type PriceHistoryResponse = Camelize<ApiPriceHistoryResponse>
12361238
export type ProfileCollectionsResponse = Camelize<ApiProfileCollectionsResponse>
12371239
export type SwapTransactionResponse = Camelize<ApiSwapTransactionResponse>
12381240
export type TokenBatchResponse = Camelize<ApiTokenBatchResponse>
1241+
export type TokenHoldersResponse = Camelize<ApiTokenHoldersResponse>
1242+
export type TokenLiquidityPoolsResponse =
1243+
Camelize<ApiTokenLiquidityPoolsResponse>
12391244
export type TokenSwapActivityPaginatedResponse =
12401245
Camelize<ApiTokenSwapActivityPaginatedResponse>
12411246
export type TransferResponse = Camelize<ApiTransferResponse>
@@ -1295,6 +1300,26 @@ export interface TokenActivityArgs {
12951300
cursor?: string
12961301
}
12971302

1303+
/**
1304+
* Query args for the token holders endpoint (paginated with `cursor`,
1305+
* sortable by `QUANTITY`).
1306+
* @category API Query Args
1307+
*/
1308+
export interface TokenHoldersArgs {
1309+
limit?: number
1310+
cursor?: string
1311+
sortBy?: "QUANTITY"
1312+
sortDirection?: "asc" | "desc"
1313+
}
1314+
1315+
/**
1316+
* Query args for the token liquidity-pools endpoint.
1317+
* @category API Query Args
1318+
*/
1319+
export interface TokenLiquidityPoolsArgs {
1320+
limit?: number
1321+
}
1322+
12981323
/**
12991324
* Query args for the NFT owners endpoint (paginated with `next` cursor).
13001325
* @category API Query Args

test/api/tokens.spec.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
GetTrendingTokensResponse,
1010
Token,
1111
} from "../../src/api/types"
12+
import { Chain } from "../../src/types"
1213
import { createMockFetcher } from "../fixtures/fetcher"
1314

1415
const mockToken: Token = {
@@ -352,6 +353,68 @@ describe("API: TokensAPI", () => {
352353
})
353354
})
354355

356+
describe("getTokenHolders", () => {
357+
test("fetches token holders without args", async () => {
358+
mockGet.mockResolvedValue({ holders: [] })
359+
360+
await tokensAPI.getTokenHolders(
361+
Chain.Mainnet,
362+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
363+
)
364+
365+
expect(mockGet).toHaveBeenCalledTimes(1)
366+
expect(mockGet.mock.calls[0][0]).toBe(
367+
"/api/v2/chain/ethereum/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/holders",
368+
)
369+
expect(mockGet.mock.calls[0][1]).toBeUndefined()
370+
})
371+
372+
test("passes limit/cursor/sortBy/sortDirection query args", async () => {
373+
mockGet.mockResolvedValue({ holders: [] })
374+
375+
await tokensAPI.getTokenHolders(Chain.Mainnet, "0xabc", {
376+
limit: 50,
377+
cursor: "page-2",
378+
sortBy: "QUANTITY",
379+
sortDirection: "desc",
380+
})
381+
382+
expect(mockGet.mock.calls[0][1]).toEqual({
383+
limit: 50,
384+
cursor: "page-2",
385+
sortBy: "QUANTITY",
386+
sortDirection: "desc",
387+
})
388+
})
389+
})
390+
391+
describe("getTokenLiquidityPools", () => {
392+
test("fetches token liquidity pools without args", async () => {
393+
mockGet.mockResolvedValue({ pools: [] })
394+
395+
await tokensAPI.getTokenLiquidityPools(
396+
Chain.Mainnet,
397+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
398+
)
399+
400+
expect(mockGet).toHaveBeenCalledTimes(1)
401+
expect(mockGet.mock.calls[0][0]).toBe(
402+
"/api/v2/chain/ethereum/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/liquidity-pools",
403+
)
404+
expect(mockGet.mock.calls[0][1]).toBeUndefined()
405+
})
406+
407+
test("passes limit query arg", async () => {
408+
mockGet.mockResolvedValue({ pools: [] })
409+
410+
await tokensAPI.getTokenLiquidityPools(Chain.Mainnet, "0xabc", {
411+
limit: 50,
412+
})
413+
414+
expect(mockGet.mock.calls[0][1]).toEqual({ limit: 50 })
415+
})
416+
})
417+
355418
describe("Constructor", () => {
356419
test("initializes with fetcher", () => {
357420
const { fetcher } = createMockFetcher()

0 commit comments

Comments
 (0)