opentool 0.19.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/dist/adapters/polymarket/index.d.ts +74 -2
- package/dist/adapters/polymarket/index.js +225 -14
- package/dist/adapters/polymarket/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +225 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/base/package.json +1 -1
- package/templates/polymarket-simple-trade/README.md +7 -3
- package/templates/polymarket-simple-trade/metadata.ts +1 -1
- package/templates/polymarket-simple-trade/package.json +1 -1
- package/templates/polymarket-simple-trade/tools/place-polymarket-order.ts +65 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Polymarket Simple Trade
|
|
2
2
|
|
|
3
|
-
Minimal OpenTool starter for placing one Polymarket order with an operating wallet signer
|
|
3
|
+
Minimal OpenTool starter for placing one Polymarket mainnet order with an operating wallet signer, a canonical Polymarket funder address, and `signatureType = 2`.
|
|
4
4
|
|
|
5
5
|
## Quickstart
|
|
6
6
|
|
|
@@ -18,7 +18,11 @@ npx opentool dev
|
|
|
18
18
|
"side": "BUY",
|
|
19
19
|
"price": "0.52",
|
|
20
20
|
"size": "10",
|
|
21
|
-
"orderType": "GTC"
|
|
22
|
-
"environment": "mainnet"
|
|
21
|
+
"orderType": "GTC"
|
|
23
22
|
}
|
|
24
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
|
|
@@ -3,7 +3,7 @@ export const metadata = {
|
|
|
3
3
|
name: "polymarket-simple-trade",
|
|
4
4
|
displayName: "Polymarket Simple Trade",
|
|
5
5
|
version: "1.0.0",
|
|
6
|
-
description: "Minimal OpenTool starter for placing one Polymarket trade.",
|
|
6
|
+
description: "Minimal OpenTool starter for placing one Polymarket trade via the canonical funder account path.",
|
|
7
7
|
category: "internal",
|
|
8
8
|
discovery: {
|
|
9
9
|
keywords: ["polymarket", "prediction-market", "trading", "opentool"],
|
|
@@ -2,12 +2,18 @@ import {
|
|
|
2
2
|
createOrDerivePolymarketApiKey,
|
|
3
3
|
placePolymarketOrder,
|
|
4
4
|
type PolymarketApiCredentials,
|
|
5
|
-
type PolymarketEnvironment,
|
|
6
5
|
} from "opentool/adapters/polymarket";
|
|
7
6
|
import { store } from "opentool/store";
|
|
8
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
wallet,
|
|
9
|
+
type WalletContext,
|
|
10
|
+
type WalletFullContext,
|
|
11
|
+
} from "opentool/wallet";
|
|
9
12
|
import { z } from "zod";
|
|
10
13
|
|
|
14
|
+
const POLYMARKET_ENVIRONMENT = "mainnet" as const;
|
|
15
|
+
const POLYMARKET_SIGNATURE_TYPE = 2 as const;
|
|
16
|
+
|
|
11
17
|
const tradeRequestSchema = z
|
|
12
18
|
.object({
|
|
13
19
|
conditionId: z.string().min(1),
|
|
@@ -16,7 +22,6 @@ const tradeRequestSchema = z
|
|
|
16
22
|
price: z.coerce.string().min(1),
|
|
17
23
|
size: z.coerce.string().min(1),
|
|
18
24
|
orderType: z.enum(["GTC", "FOK", "FAK", "GTD"]).default("GTC"),
|
|
19
|
-
environment: z.enum(["mainnet", "testnet"]).optional(),
|
|
20
25
|
expiration: z.coerce.number().int().nonnegative().optional(),
|
|
21
26
|
nonce: z.coerce.number().int().nonnegative().optional(),
|
|
22
27
|
feeRateBps: z.coerce.number().int().nonnegative().optional(),
|
|
@@ -25,13 +30,23 @@ const tradeRequestSchema = z
|
|
|
25
30
|
.strict();
|
|
26
31
|
|
|
27
32
|
export const profile = {
|
|
28
|
-
description:
|
|
33
|
+
description:
|
|
34
|
+
"Place one Polymarket order with the operating wallet signer and canonical funder account",
|
|
29
35
|
category: "trade",
|
|
30
36
|
};
|
|
31
37
|
|
|
32
|
-
function assertSignerContext(
|
|
33
|
-
|
|
34
|
-
|
|
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
|
+
);
|
|
35
50
|
}
|
|
36
51
|
}
|
|
37
52
|
|
|
@@ -49,26 +64,49 @@ function readCredentialsFromEnv(): PolymarketApiCredentials | undefined {
|
|
|
49
64
|
};
|
|
50
65
|
}
|
|
51
66
|
|
|
52
|
-
function
|
|
53
|
-
|
|
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;
|
|
54
90
|
}
|
|
55
91
|
|
|
56
92
|
export async function POST(req: Request) {
|
|
57
93
|
const payload = tradeRequestSchema.parse(await req.json());
|
|
58
|
-
const environment
|
|
94
|
+
const environment = POLYMARKET_ENVIRONMENT;
|
|
59
95
|
const ctx = await wallet({
|
|
60
96
|
chain: process.env.OPENTOOL_SIGNER_CHAIN ?? "base",
|
|
61
97
|
apiKey: process.env.ALCHEMY_API_KEY,
|
|
62
98
|
rpcUrl: process.env.RPC_URL,
|
|
63
99
|
});
|
|
64
100
|
assertSignerContext(ctx);
|
|
101
|
+
const funderAddress = readFunderAddress();
|
|
102
|
+
const apiKeyNonce = readApiKeyNonce();
|
|
65
103
|
|
|
66
|
-
// Polymarket only needs EIP-712 signing here, so the signer can come from the shared operating wallet.
|
|
67
104
|
const credentials =
|
|
68
105
|
readCredentialsFromEnv() ??
|
|
69
106
|
(await createOrDerivePolymarketApiKey({
|
|
70
107
|
wallet: ctx,
|
|
71
108
|
environment,
|
|
109
|
+
nonce: apiKeyNonce,
|
|
72
110
|
}));
|
|
73
111
|
|
|
74
112
|
const result = await placePolymarketOrder({
|
|
@@ -81,19 +119,27 @@ export async function POST(req: Request) {
|
|
|
81
119
|
side: payload.side,
|
|
82
120
|
price: payload.price,
|
|
83
121
|
size: payload.size,
|
|
84
|
-
|
|
122
|
+
maker: funderAddress,
|
|
123
|
+
signer: ctx.address as `0x${string}`,
|
|
124
|
+
signatureType: POLYMARKET_SIGNATURE_TYPE,
|
|
125
|
+
...(payload.expiration !== undefined
|
|
126
|
+
? { expiration: payload.expiration }
|
|
127
|
+
: {}),
|
|
85
128
|
...(payload.nonce !== undefined ? { nonce: payload.nonce } : {}),
|
|
86
|
-
...(payload.feeRateBps !== undefined
|
|
129
|
+
...(payload.feeRateBps !== undefined
|
|
130
|
+
? { feeRateBps: payload.feeRateBps }
|
|
131
|
+
: {}),
|
|
87
132
|
...(payload.tickSize ? { tickSize: payload.tickSize } : {}),
|
|
88
133
|
},
|
|
89
134
|
});
|
|
90
135
|
|
|
91
|
-
const ref =
|
|
136
|
+
const ref =
|
|
137
|
+
result.orderId ?? `${payload.conditionId}:${payload.tokenId}:${Date.now()}`;
|
|
92
138
|
await store({
|
|
93
139
|
source: "polymarket",
|
|
94
140
|
ref,
|
|
95
141
|
status: "submitted",
|
|
96
|
-
walletAddress:
|
|
142
|
+
walletAddress: funderAddress,
|
|
97
143
|
action: "trade",
|
|
98
144
|
notional: payload.size,
|
|
99
145
|
market: {
|
|
@@ -111,12 +157,16 @@ export async function POST(req: Request) {
|
|
|
111
157
|
price: payload.price,
|
|
112
158
|
size: payload.size,
|
|
113
159
|
orderType: payload.orderType,
|
|
160
|
+
signerAddress: ctx.address,
|
|
161
|
+
funderAddress,
|
|
162
|
+
signatureType: POLYMARKET_SIGNATURE_TYPE,
|
|
114
163
|
},
|
|
115
164
|
});
|
|
116
165
|
|
|
117
166
|
return Response.json({
|
|
118
167
|
ok: true,
|
|
119
168
|
environment,
|
|
169
|
+
funderAddress,
|
|
120
170
|
order: result,
|
|
121
171
|
});
|
|
122
172
|
}
|