opentool 0.18.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opentool",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
4
  "description": "OpenTool framework for building serverless MCP tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -10,7 +10,7 @@
10
10
  "validate": "opentool validate"
11
11
  },
12
12
  "dependencies": {
13
- "opentool": "^0.18.0",
13
+ "opentool": "^0.19.1",
14
14
  "zod": "^4.3.6"
15
15
  },
16
16
  "devDependencies": {
@@ -0,0 +1,28 @@
1
+ # Polymarket Simple Trade
2
+
3
+ Minimal OpenTool starter for placing one Polymarket mainnet order with an operating wallet signer, a canonical Polymarket funder address, and `signatureType = 2`.
4
+
5
+ ## Quickstart
6
+
7
+ ```bash
8
+ npm install
9
+ npx opentool dev
10
+ ```
11
+
12
+ ## Request Example
13
+
14
+ ```json
15
+ {
16
+ "conditionId": "0xcondition",
17
+ "tokenId": "123456789",
18
+ "side": "BUY",
19
+ "price": "0.52",
20
+ "size": "10",
21
+ "orderType": "GTC"
22
+ }
23
+ ```
24
+
25
+ ## Required Runtime
26
+
27
+ - `POLYMARKET_FUNDER_ADDRESS`: the user's Polymarket proxy/safe trading wallet
28
+ - Turnkey signer envs injected by the platform runtime
@@ -0,0 +1,12 @@
1
+ export const metadata = {
2
+ metadataSpecVersion: "1.0.0",
3
+ name: "polymarket-simple-trade",
4
+ displayName: "Polymarket Simple Trade",
5
+ version: "1.0.0",
6
+ description: "Minimal OpenTool starter for placing one Polymarket trade via the canonical funder account path.",
7
+ category: "internal",
8
+ discovery: {
9
+ keywords: ["polymarket", "prediction-market", "trading", "opentool"],
10
+ category: "trading-tools",
11
+ },
12
+ };
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "polymarket-simple-trade",
3
+ "version": "1.0.0",
4
+ "description": "Minimal OpenTool starter for placing one Polymarket trade",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "opentool dev",
9
+ "build": "opentool build",
10
+ "validate": "opentool validate"
11
+ },
12
+ "dependencies": {
13
+ "opentool": "^0.19.1",
14
+ "zod": "^4.3.6"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.0.0",
18
+ "@types/node": "^20.0.0"
19
+ },
20
+ "overrides": {
21
+ "esbuild": "^0.25.12"
22
+ }
23
+ }
@@ -0,0 +1,172 @@
1
+ import {
2
+ createOrDerivePolymarketApiKey,
3
+ placePolymarketOrder,
4
+ type PolymarketApiCredentials,
5
+ } from "opentool/adapters/polymarket";
6
+ import { store } from "opentool/store";
7
+ import {
8
+ wallet,
9
+ type WalletContext,
10
+ type WalletFullContext,
11
+ } from "opentool/wallet";
12
+ import { z } from "zod";
13
+
14
+ const POLYMARKET_ENVIRONMENT = "mainnet" as const;
15
+ const POLYMARKET_SIGNATURE_TYPE = 2 as const;
16
+
17
+ const tradeRequestSchema = z
18
+ .object({
19
+ conditionId: z.string().min(1),
20
+ tokenId: z.string().min(1),
21
+ side: z.enum(["BUY", "SELL"]),
22
+ price: z.coerce.string().min(1),
23
+ size: z.coerce.string().min(1),
24
+ orderType: z.enum(["GTC", "FOK", "FAK", "GTD"]).default("GTC"),
25
+ expiration: z.coerce.number().int().nonnegative().optional(),
26
+ nonce: z.coerce.number().int().nonnegative().optional(),
27
+ feeRateBps: z.coerce.number().int().nonnegative().optional(),
28
+ tickSize: z.coerce.string().min(1).optional(),
29
+ })
30
+ .strict();
31
+
32
+ export const profile = {
33
+ description:
34
+ "Place one Polymarket order with the operating wallet signer and canonical funder account",
35
+ category: "trade",
36
+ };
37
+
38
+ function assertSignerContext(
39
+ ctx: WalletContext,
40
+ ): asserts ctx is WalletFullContext {
41
+ if (
42
+ !("walletClient" in ctx) ||
43
+ !ctx.walletClient ||
44
+ !("account" in ctx) ||
45
+ !ctx.account
46
+ ) {
47
+ throw new Error(
48
+ "Configure a signer (PRIVATE_KEY or Turnkey env vars) before trading.",
49
+ );
50
+ }
51
+ }
52
+
53
+ function readCredentialsFromEnv(): PolymarketApiCredentials | undefined {
54
+ const apiKey = process.env.POLYMARKET_API_KEY?.trim();
55
+ const secret = process.env.POLYMARKET_API_SECRET?.trim();
56
+ const passphrase = process.env.POLYMARKET_API_PASSPHRASE?.trim();
57
+ if (!apiKey || !secret || !passphrase) {
58
+ return undefined;
59
+ }
60
+ return {
61
+ apiKey,
62
+ secret,
63
+ passphrase,
64
+ };
65
+ }
66
+
67
+ function readFunderAddress(): `0x${string}` {
68
+ const funderAddress = process.env.POLYMARKET_FUNDER_ADDRESS?.trim();
69
+ if (!funderAddress || !/^0x[a-fA-F0-9]{40}$/.test(funderAddress)) {
70
+ throw new Error(
71
+ "POLYMARKET_FUNDER_ADDRESS must be set to the user's Polymarket funder wallet.",
72
+ );
73
+ }
74
+ return funderAddress as `0x${string}`;
75
+ }
76
+
77
+ function readApiKeyNonce(): number {
78
+ const rawNonce = process.env.POLYMARKET_API_NONCE?.trim();
79
+ if (!rawNonce) {
80
+ throw new Error("POLYMARKET_API_NONCE must be set for Polymarket trading.");
81
+ }
82
+ if (!/^\d+$/.test(rawNonce)) {
83
+ throw new Error("POLYMARKET_API_NONCE must be a non-negative integer.");
84
+ }
85
+ const nonce = Number(rawNonce);
86
+ if (!Number.isSafeInteger(nonce) || nonce < 0) {
87
+ throw new Error("POLYMARKET_API_NONCE must be a non-negative integer.");
88
+ }
89
+ return nonce;
90
+ }
91
+
92
+ export async function POST(req: Request) {
93
+ const payload = tradeRequestSchema.parse(await req.json());
94
+ const environment = POLYMARKET_ENVIRONMENT;
95
+ const ctx = await wallet({
96
+ chain: process.env.OPENTOOL_SIGNER_CHAIN ?? "base",
97
+ apiKey: process.env.ALCHEMY_API_KEY,
98
+ rpcUrl: process.env.RPC_URL,
99
+ });
100
+ assertSignerContext(ctx);
101
+ const funderAddress = readFunderAddress();
102
+ const apiKeyNonce = readApiKeyNonce();
103
+
104
+ const credentials =
105
+ readCredentialsFromEnv() ??
106
+ (await createOrDerivePolymarketApiKey({
107
+ wallet: ctx,
108
+ environment,
109
+ nonce: apiKeyNonce,
110
+ }));
111
+
112
+ const result = await placePolymarketOrder({
113
+ wallet: ctx,
114
+ credentials,
115
+ environment,
116
+ orderType: payload.orderType,
117
+ order: {
118
+ tokenId: payload.tokenId,
119
+ side: payload.side,
120
+ price: payload.price,
121
+ size: payload.size,
122
+ maker: funderAddress,
123
+ signer: ctx.address as `0x${string}`,
124
+ signatureType: POLYMARKET_SIGNATURE_TYPE,
125
+ ...(payload.expiration !== undefined
126
+ ? { expiration: payload.expiration }
127
+ : {}),
128
+ ...(payload.nonce !== undefined ? { nonce: payload.nonce } : {}),
129
+ ...(payload.feeRateBps !== undefined
130
+ ? { feeRateBps: payload.feeRateBps }
131
+ : {}),
132
+ ...(payload.tickSize ? { tickSize: payload.tickSize } : {}),
133
+ },
134
+ });
135
+
136
+ const ref =
137
+ result.orderId ?? `${payload.conditionId}:${payload.tokenId}:${Date.now()}`;
138
+ await store({
139
+ source: "polymarket",
140
+ ref,
141
+ status: "submitted",
142
+ walletAddress: funderAddress,
143
+ action: "trade",
144
+ notional: payload.size,
145
+ market: {
146
+ market_type: "prediction",
147
+ venue: "polymarket",
148
+ environment,
149
+ canonical_symbol: `${payload.conditionId}:${payload.tokenId}`,
150
+ },
151
+ metadata: {
152
+ tool: "place-polymarket-order",
153
+ orderId: result.orderId ?? null,
154
+ conditionId: payload.conditionId,
155
+ tokenId: payload.tokenId,
156
+ side: payload.side,
157
+ price: payload.price,
158
+ size: payload.size,
159
+ orderType: payload.orderType,
160
+ signerAddress: ctx.address,
161
+ funderAddress,
162
+ signatureType: POLYMARKET_SIGNATURE_TYPE,
163
+ },
164
+ });
165
+
166
+ return Response.json({
167
+ ok: true,
168
+ environment,
169
+ funderAddress,
170
+ order: result,
171
+ });
172
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2020"],
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "resolveJsonModule": true,
11
+ "allowSyntheticDefaultImports": true
12
+ },
13
+ "include": ["tools/**/*.ts"]
14
+ }