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
10 changes: 9 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
# Polymarket SDK Test Configuration

# Private key for testing (DO NOT commit real private key)
PRIVATE_KEY=0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE

# Optional: Signature type for trading
# 0 = EOA / browser wallet
# 1 = Magic / Email login (requires POLYMARKET_FUNDER)
# POLYMARKET_SIGNATURE_TYPE=1

# Optional: Polymarket profile / funder address for Magic / Email login
# POLYMARKET_FUNDER=0xYourPolymarketProfileAddress

# Optional: Market configuration for tests
# MARKET_CONDITION_ID=0x4e605132e536d51c37a28cdc0ac77e48c77d8e2251743d4eae3309165dee7d34
Expand Down
4 changes: 4 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -eu

node scripts/check-secrets.mjs --staged
4 changes: 4 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
set -eu

node scripts/check-secrets.mjs --push
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ coverage/
# Cache
.cache/
*.tsbuildinfo
.mcp.json.test-creds.json
.mcp.json
.test-creds.json

# Keys
*.pem
4 changes: 2 additions & 2 deletions .mcp.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"mcpServers": {
"polymarket": {
"command": "node",
"args": ["/Users/hhh0x/meme/PredictionRouterV1/PerdictionRouter/packages/poly-mcp/dist/server.js"],
"args": ["/path/to/poly-mcp/dist/server.js"],
"env": {
"POLY_PRIVATE_KEY": "0x534f580d171686a880db4a5b09fc5981df16073f016c933b3e92ce3bc049019d"
"POLY_PRIVATE_KEY": "0xYOUR_PRIVATE_KEY_HERE"
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions .test-creds.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"key": "YOUR_API_KEY_HERE",
"secret": "YOUR_API_SECRET_HERE",
"passphrase": "YOUR_API_PASSPHRASE_HERE",
"walletAddress": "0xYOUR_WALLET_ADDRESS_HERE",
"derivedAt": "1970-01-01T00:00:00.000Z"
}
7 changes: 0 additions & 7 deletions .test-creds.json

This file was deleted.

43 changes: 35 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,13 @@ const sdk = new PolymarketSDK();

// Get market by slug or condition ID
const market = await sdk.getMarket('will-trump-win-2024');
const binary = getBinaryTokens(market.tokens);
if (!binary) throw new Error('Expected binary market');
const yesToken = binary.primary;
const noToken = binary.secondary;
console.log(`${market.question}`);
console.log(`YES: ${market.tokens.find(t => t.outcome === 'Yes')?.price}`);
console.log(`NO: ${market.tokens.find(t => t.outcome === 'No')?.price}`);
console.log(`YES (${yesToken.outcome}): ${yesToken.price}`);
console.log(`NO (${noToken.outcome}): ${noToken.price}`);

// Get processed orderbook with analytics
const orderbook = await sdk.getOrderbook(market.conditionId);
Expand Down Expand Up @@ -223,6 +227,28 @@ console.log(`Open orders: ${openOrders.length}`);
sdk.stop();
```

### Email / Magic Login (funder account)

```typescript
import { PolymarketSDK, getBinaryTokens } from '@catalyst-team/poly-sdk';

const sdk = await PolymarketSDK.create({
privateKey: process.env.POLYMARKET_PRIVATE_KEY!, // Exported signer private key
signatureType: 1, // Magic / Email login
funderAddress: process.env.POLYMARKET_FUNDER!, // Polymarket profile address shown in the UI
});

const order = await sdk.tradingService.createLimitOrder({
tokenId: yesTokenId,
side: 'BUY',
price: 0.45,
size: 10,
orderType: 'GTC',
});

sdk.stop();
```

---

## Services Guide
Expand Down Expand Up @@ -811,8 +837,9 @@ const noPrice = market.tokens.no.price;
**After (v0.3.0)**:
```typescript
// Array of MarketToken objects
const yesToken = market.tokens.find(t => t.outcome === 'Yes');
const noToken = market.tokens.find(t => t.outcome === 'No');
const binary = getBinaryTokens(market.tokens);
const yesToken = binary?.primary;
const noToken = binary?.secondary;

const yesPrice = yesToken?.price;
const noPrice = noToken?.price;
Expand All @@ -822,13 +849,13 @@ const noPrice = noToken?.price;

```typescript
// Helper function for migration
function getTokenPrice(market: UnifiedMarket, outcome: 'Yes' | 'No'): number {
return market.tokens.find(t => t.outcome === outcome)?.price ?? 0;
function getTokenPrice(market: UnifiedMarket, side: 'yes' | 'no'): number {
return market.tokens[side === 'yes' ? 0 : 1]?.price ?? 0;
}

// Usage
const yesPrice = getTokenPrice(market, 'Yes');
const noPrice = getTokenPrice(market, 'No');
const yesPrice = getTokenPrice(market, 'yes');
const noPrice = getTokenPrice(market, 'no');
```

**Why the change?** The array format better supports multi-outcome markets and is more consistent with the Polymarket API response format.
Expand Down
43 changes: 35 additions & 8 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,13 @@ const sdk = new PolymarketSDK();

// 通过 slug 或 condition ID 获取市场
const market = await sdk.getMarket('will-trump-win-2024');
const binary = getBinaryTokens(market.tokens);
if (!binary) throw new Error('Expected binary market');
const yesToken = binary.primary;
const noToken = binary.secondary;
console.log(`${market.question}`);
console.log(`YES: ${market.tokens.find(t => t.outcome === 'Yes')?.price}`);
console.log(`NO: ${market.tokens.find(t => t.outcome === 'No')?.price}`);
console.log(`YES (${yesToken.outcome}): ${yesToken.price}`);
console.log(`NO (${noToken.outcome}): ${noToken.price}`);

// 获取处理后的订单簿(含分析数据)
const orderbook = await sdk.getOrderbook(market.conditionId);
Expand Down Expand Up @@ -215,6 +219,28 @@ console.log(`未成交订单: ${openOrders.length}`);
sdk.stop();
```

### Email / Magic 登录(funder account)

```typescript
import { PolymarketSDK, getBinaryTokens } from '@catalyst-team/poly-sdk';

const sdk = await PolymarketSDK.create({
privateKey: process.env.POLYMARKET_PRIVATE_KEY!, // 导出的 signer 私钥
signatureType: 1, // Magic / Email 登录
funderAddress: process.env.POLYMARKET_FUNDER!, // Polymarket 页面显示的 profile address
});

const order = await sdk.tradingService.createLimitOrder({
tokenId: yesTokenId,
side: 'BUY',
price: 0.45,
size: 10,
orderType: 'GTC',
});

sdk.stop();
```

---

## 服务指南
Expand Down Expand Up @@ -684,8 +710,9 @@ const noPrice = market.tokens.no.price;
**之后 (v0.3.0)**:
```typescript
// MarketToken 对象数组
const yesToken = market.tokens.find(t => t.outcome === 'Yes');
const noToken = market.tokens.find(t => t.outcome === 'No');
const binary = getBinaryTokens(market.tokens);
const yesToken = binary?.primary;
const noToken = binary?.secondary;

const yesPrice = yesToken?.price;
const noPrice = noToken?.price;
Expand All @@ -695,13 +722,13 @@ const noPrice = noToken?.price;

```typescript
// 迁移辅助函数
function getTokenPrice(market: UnifiedMarket, outcome: 'Yes' | 'No'): number {
return market.tokens.find(t => t.outcome === outcome)?.price ?? 0;
function getTokenPrice(market: UnifiedMarket, side: 'yes' | 'no'): number {
return market.tokens[side === 'yes' ? 0 : 1]?.price ?? 0;
}

// 使用
const yesPrice = getTokenPrice(market, 'Yes');
const noPrice = getTokenPrice(market, 'No');
const yesPrice = getTokenPrice(market, 'yes');
const noPrice = getTokenPrice(market, 'no');
```

**为什么改变?** 数组格式更好地支持多结果市场,并且与 Polymarket API 响应格式更一致。
Expand Down
14 changes: 10 additions & 4 deletions docs/api/01-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,8 +659,10 @@ import type { TokenIds } from '@catalyst-team/poly-sdk';

// Get token IDs from CLOB API
const market = await clobApi.getMarket('0xabc123...');
const yesToken = market.tokens.find(t => t.outcome === 'Yes');
const noToken = market.tokens.find(t => t.outcome === 'No');
const [yesToken, noToken] = market.tokens;
if (!yesToken || !noToken || market.tokens.length !== 2) {
throw new Error('Expected binary market');
}

const tokenIds: TokenIds = {
yesTokenId: yesToken.tokenId,
Expand Down Expand Up @@ -825,9 +827,13 @@ const clobApi = new ClobApiClient(rateLimiter, cache);

// 1. Get market and token IDs
const market = await clobApi.getMarket(conditionId);
const [yesToken, noToken] = market.tokens;
if (!yesToken || !noToken || market.tokens.length !== 2) {
throw new Error('Expected binary market');
}
const tokenIds: TokenIds = {
yesTokenId: market.tokens.find(t => t.outcome === 'Yes').tokenId,
noTokenId: market.tokens.find(t => t.outcome === 'No').tokenId,
yesTokenId: yesToken.tokenId,
noTokenId: noToken.tokenId,
};

// 2. Get processed orderbook
Expand Down
8 changes: 4 additions & 4 deletions docs/private-key-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

```typescript
// ❌ 不安全的旧方式
const PRIVATE_KEY = process.env.PRIVATE_KEY || '0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459';
const PRIVATE_KEY = process.env.PRIVATE_KEY || '0xYOUR_PRIVATE_KEY_HERE';
```

这种做法存在严重安全隐患:
Expand Down Expand Up @@ -43,7 +43,7 @@ if (!PRIVATE_KEY) {
# Polymarket SDK Test Configuration

# Private key for testing (DO NOT commit real private key)
PRIVATE_KEY=0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE

# Optional: Market configuration for tests
# MARKET_CONDITION_ID=0x...
Expand All @@ -56,7 +56,7 @@ PRIVATE_KEY=0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459
# Polymarket SDK Test Configuration

# Private key for testing
PRIVATE_KEY=0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE

# Optional: Market configuration for tests
# MARKET_CONDITION_ID=0x4e605132e536d51c37a28cdc0ac77e48c77d8e2251743d4eae3309165dee7d34
Expand Down Expand Up @@ -105,7 +105,7 @@ PRIVATE_KEY=0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459

```bash
# 搜索所有脚本中是否还有硬编码私钥
grep -r "0xa1c847feeb03315d72a09481edae8e34d3892ae281b6b2291911380490a23459" scripts/ | grep -v ".env"
grep -r "0xYOUR_PRIVATE_KEY_HERE" scripts/ | grep -v ".env"
# 输出: ✓ No hardcoded private keys found in scripts
```

Expand Down
20 changes: 10 additions & 10 deletions docs/test/e2e-test-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ describe('TradingService Integration', () => {
testMarketConditionId = markets[0].conditionId;

const market = await service.getMarket(testMarketConditionId);
testTokenId = market.tokens.find(t => t.outcome === 'Yes')?.tokenId;
testTokenId = market.tokens[0]?.tokenId;
});

describe('getMarket', () => {
Expand All @@ -105,8 +105,8 @@ describe('TradingService Integration', () => {
expect(market.conditionId).toBe(testMarketConditionId);
expect(typeof market.question).toBe('string');
expect(market.tokens.length).toBeGreaterThanOrEqual(2);
expect(market.tokens.some(t => t.outcome === 'Yes')).toBe(true);
expect(market.tokens.some(t => t.outcome === 'No')).toBe(true);
expect(market.tokens[0]?.tokenId).toBeDefined();
expect(market.tokens[1]?.tokenId).toBeDefined();
});
});

Expand Down Expand Up @@ -259,8 +259,8 @@ describe('RealtimeServiceV2 Integration', () => {
`https://clob.polymarket.com/markets/${conditionId}`
);
const market = await clobResponse.json();
yesTokenId = market.tokens.find((t: any) => t.outcome === 'Yes').token_id;
noTokenId = market.tokens.find((t: any) => t.outcome === 'No').token_id;
yesTokenId = market.tokens[0].token_id;
noTokenId = market.tokens[1].token_id;
});

beforeEach(() => {
Expand Down Expand Up @@ -454,8 +454,8 @@ describe('ArbitrageService Integration', () => {
testMarket = {
name: question.slice(0, 50),
conditionId,
yesTokenId: market.tokens.find((t: any) => t.outcome === 'Yes').token_id,
noTokenId: market.tokens.find((t: any) => t.outcome === 'No').token_id,
yesTokenId: market.tokens[0].token_id,
noTokenId: market.tokens[1].token_id,
};
});

Expand Down Expand Up @@ -624,7 +624,7 @@ describe('RealtimeService (Legacy) Integration', () => {
`https://clob.polymarket.com/markets/${conditionId}`
);
const market = await clobResponse.json();
testTokenId = market.tokens.find((t: any) => t.outcome === 'Yes').token_id;
testTokenId = market.tokens[0].token_id;
});

beforeEach(() => {
Expand Down Expand Up @@ -795,8 +795,8 @@ export async function getTestMarket(): Promise<{

return {
conditionId,
yesTokenId: market.tokens.find((t: any) => t.outcome === 'Yes').token_id,
noTokenId: market.tokens.find((t: any) => t.outcome === 'No').token_id,
yesTokenId: market.tokens[0].token_id,
noTokenId: market.tokens[1].token_id,
question,
};
}
Expand Down
Loading