@circuitorg/agent-sdk 1.1.7 โ 1.2.0
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/README.md +358 -751
- package/index.d.ts +1294 -448
- package/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,45 @@
|
|
|
1
|
-
# Circuit Agent SDK -
|
|
1
|
+
# Circuit Agent SDK - TypeScript
|
|
2
2
|
|
|
3
|
-
> **Clean, type-safe TypeScript SDK for building cross-chain agents on
|
|
3
|
+
> **Clean, unified, type-safe TypeScript SDK for building cross-chain agents on Circuit**
|
|
4
4
|
|
|
5
|
-
A TypeScript SDK for building automated agents to deploy on Circuit.
|
|
5
|
+
A simplified TypeScript SDK for building automated agents to deploy on Circuit. Agents receive a single `AgentContext` object containing everything they need - request data, SDK methods, and unified logging. No boilerplate, no return values to manage, just write your logic.
|
|
6
6
|
|
|
7
7
|
> **๐ก Best used with [Circuit Agents CLI](https://github.com/circuitorg/agents-cli)** - Deploy, manage, and test your agents with ease
|
|
8
8
|
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
9
|
+
## ๐ Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Circuit Agent SDK - TypeScript](#circuit-agent-sdk---typescript)
|
|
12
|
+
- [๐ Table of Contents](#-table-of-contents)
|
|
13
|
+
- [๐ Quick Start](#-quick-start)
|
|
14
|
+
- [Install the SDK](#install-the-sdk)
|
|
15
|
+
- [Create Your First Agent](#create-your-first-agent)
|
|
16
|
+
- [๐ฏ Core Concepts](#-core-concepts)
|
|
17
|
+
- [The AgentContext Object](#the-agentcontext-object)
|
|
18
|
+
- [Run/Stop Function Requirements](#runstop-function-requirements)
|
|
19
|
+
- [๐ Unified Logging with agent.log()](#-unified-logging-with-agentlog)
|
|
20
|
+
- [๐พ Session Memory Storage](#-session-memory-storage)
|
|
21
|
+
- [๐ Cross-Chain Swaps with Swidge](#-cross-chain-swaps-with-swidge)
|
|
22
|
+
- [Get and execute a quote](#get-and-execute-a-quote)
|
|
23
|
+
- [๐ Polymarket Prediction Markets](#-polymarket-prediction-markets)
|
|
24
|
+
- [Place Market Orders](#place-market-orders)
|
|
25
|
+
- [Redeem Positions](#redeem-positions)
|
|
26
|
+
- [๐ Sign \& Send Transactions](#-sign--send-transactions)
|
|
27
|
+
- [Ethereum (any EVM chain)](#ethereum-any-evm-chain)
|
|
28
|
+
- [Solana](#solana)
|
|
29
|
+
- [โก Error Handling](#-error-handling)
|
|
30
|
+
- [๐ ๏ธ Deployment](#๏ธ-deployment)
|
|
31
|
+
- [What You Write](#what-you-write)
|
|
32
|
+
- [Deploy to Circuit](#deploy-to-circuit)
|
|
33
|
+
- [Test Locally](#test-locally)
|
|
34
|
+
- [๐งช Manual Instantiation (Testing)](#-manual-instantiation-testing)
|
|
35
|
+
- [๐ Working Examples](#-working-examples)
|
|
36
|
+
- [`demo-agent.ts`](#demo-agentts)
|
|
37
|
+
- [`examples/kitchen-sink.ts`](#exampleskitchen-sinkts)
|
|
38
|
+
- [๐ฏ Key Takeaways](#-key-takeaways)
|
|
39
|
+
- [๐ Additional Resources](#-additional-resources)
|
|
16
40
|
|
|
17
41
|
## ๐ Quick Start
|
|
42
|
+
|
|
18
43
|
### Install the SDK
|
|
19
44
|
```bash
|
|
20
45
|
npm install @circuitorg/agent-sdk
|
|
@@ -24,867 +49,449 @@ yarn add @circuitorg/agent-sdk
|
|
|
24
49
|
bun add @circuitorg/agent-sdk
|
|
25
50
|
```
|
|
26
51
|
|
|
27
|
-
###
|
|
28
|
-
>**NOTE:** The fastest, and recommended, way to get started is to setup an agent via the circuit [CLI](https://github.com/circuitorg/agents-cli)'s 'circuit agent init' command. This will setup a sample agent directory with the necessary agent wireframe, and configure the cli to allow you for easy testing and deployment. You just simply need to add in your secret formula to the execution and stop functions.
|
|
52
|
+
### Create Your First Agent
|
|
29
53
|
|
|
30
|
-
|
|
31
|
-
import { AgentSdk } from "@circuitorg/agent-sdk";
|
|
54
|
+
Every agent receives a single `AgentContext` object that provides:
|
|
32
55
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```
|
|
56
|
+
**Session Data:**
|
|
57
|
+
- `agent.sessionId` - Unique session identifier
|
|
58
|
+
- `agent.sessionWalletAddress` - The wallet address for this session
|
|
59
|
+
- `agent.currentPositions` - Assets allocated to the agent at the start of this execution
|
|
38
60
|
|
|
39
|
-
|
|
61
|
+
**Available Methods:**
|
|
62
|
+
- `agent.log()` - Send messages to users and log locally
|
|
63
|
+
- `agent.memory` - Persist data across executions (`.set()`, `.get()`, `.list()`, `.delete()`)
|
|
64
|
+
- `agent.platforms.polymarket` - Trade prediction markets (`.marketOrder()`, `.redeemPositions()`)
|
|
65
|
+
- `agent.swidge` - Cross-chain swaps and bridges (`.quote()`, `.execute()`)
|
|
66
|
+
- `agent.signAndSend()` - Execute custom built transactions on any supported chain
|
|
67
|
+
- `agent.signMessage()` - Sign messages (EVM only)
|
|
40
68
|
|
|
41
|
-
|
|
69
|
+
**Important:** `currentPositions` reflects your allocated assets at the **start** of each execution. To avoid failed transactions, you should be tracking intra-run position changes based on transactions the agent makes during the execution. Circuit will provide updated positions on the next execution request.
|
|
42
70
|
|
|
43
71
|
```typescript
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
import { Agent, AgentContext } from "@circuitorg/agent-sdk";
|
|
73
|
+
|
|
74
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
75
|
+
/**
|
|
76
|
+
* Main agent logic - receives AgentContext with everything needed.
|
|
77
|
+
* No return value - errors are caught automatically.
|
|
78
|
+
*/
|
|
79
|
+
// Access session data
|
|
80
|
+
await agent.log(`Starting execution for session ${agent.sessionId}`);
|
|
81
|
+
await agent.log(`Wallet: ${agent.sessionWalletAddress}`);
|
|
82
|
+
await agent.log(`Managing ${agent.currentPositions.length} allocated positions`);
|
|
83
|
+
|
|
84
|
+
// Use SDK methods
|
|
85
|
+
await agent.memory.set("last_run", Date.now().toString());
|
|
86
|
+
|
|
87
|
+
// Your agent logic here - track position changes within this execution
|
|
88
|
+
// Circuit will provide updated positions on the next run
|
|
89
|
+
}
|
|
53
90
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
await
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
toAddress: "0x742d35cc6634C0532925a3b8D65e95f32B6b5582" as `0x${string}`,
|
|
60
|
-
data: "0x" as `0x${string}`,
|
|
61
|
-
value: "1000000000000000000" // 1 ETH in wei
|
|
62
|
-
},
|
|
63
|
-
message: "Sending 1 ETH"
|
|
64
|
-
});
|
|
91
|
+
async function stop(agent: AgentContext): Promise<void> {
|
|
92
|
+
/**Cleanup when agent is stopped.*/
|
|
93
|
+
await agent.log("Cleaning up resources");
|
|
94
|
+
await agent.memory.delete("temp_data");
|
|
95
|
+
}
|
|
65
96
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
toAddress: "0xTokenContract..." as `0x${string}`,
|
|
71
|
-
data: "0xa9059cbb..." as `0x${string}`, // encoded transfer()
|
|
72
|
-
value: "0"
|
|
73
|
-
}
|
|
97
|
+
// Boilerplate - This should never change unless you want to change the names of your run and stop functions
|
|
98
|
+
const agent = new Agent({
|
|
99
|
+
runFunction: run,
|
|
100
|
+
stopFunction: stop
|
|
74
101
|
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
#### Solana
|
|
78
102
|
|
|
79
|
-
|
|
80
|
-
await sdk.signAndSend({
|
|
81
|
-
network: "solana",
|
|
82
|
-
request: {
|
|
83
|
-
hexTransaction: "010001030a0b..." // serialized VersionedTransaction
|
|
84
|
-
}
|
|
85
|
-
});
|
|
103
|
+
export default agent.getExport();
|
|
86
104
|
```
|
|
87
105
|
|
|
106
|
+
## ๐ฏ Core Concepts
|
|
88
107
|
|
|
89
|
-
|
|
108
|
+
### The AgentContext Object
|
|
90
109
|
|
|
91
|
-
|
|
110
|
+
Every agent function receives a single `AgentContext` object that contains:
|
|
92
111
|
|
|
93
|
-
|
|
112
|
+
**Request Data:**
|
|
113
|
+
- `agent.sessionId` - Unique session identifier
|
|
114
|
+
- `agent.sessionWalletAddress` - Wallet address for this session
|
|
115
|
+
- `agent.currentPositions` - Current positions allocated to this agent
|
|
94
116
|
|
|
95
|
-
|
|
117
|
+
**SDK Methods:**
|
|
118
|
+
- `agent.log()` - Unified logging (console + backend)
|
|
119
|
+
- `agent.memory` - Session-scoped key-value storage
|
|
120
|
+
- `agent.platforms.polymarket` - Prediction market operations
|
|
121
|
+
- `agent.swidge` - Cross-chain swap operations
|
|
122
|
+
- `agent.signAndSend()` - Sign and broadcast transactions
|
|
123
|
+
- `agent.signMessage()` - Sign messages on EVM
|
|
96
124
|
|
|
97
|
-
|
|
125
|
+
### Run/Stop Function Requirements
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
from: { network: "ethereum:137", address: request.sessionWalletAddress },
|
|
103
|
-
to: { network: "ethereum:42161", address: request.sessionWalletAddress },
|
|
104
|
-
amount: "50000000", // $50 USDC (6 decimals)
|
|
105
|
-
fromToken: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC on Polygon
|
|
106
|
-
toToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
|
|
107
|
-
slippage: "2.0", // 2% slippage for cross-chain (default: 0.5%)
|
|
108
|
-
priceImpact: "1.0" // 1% max price impact (default: 0.5%)
|
|
109
|
-
});
|
|
127
|
+
1. **Signature**: `async function myFunction(agent: AgentContext): Promise<void>`
|
|
128
|
+
2. **Return**: Always return `void` (or no return statement)
|
|
129
|
+
3. **Errors**: You should surface any relevant errors via `agent.log('error message', { error: true })`. All errors from built-in SDK functions will be caught gracefully and provided in the return data for you to handle as necessary.
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
const swapQuote = await sdk.swidge.quote({
|
|
113
|
-
from: { network: "ethereum:42161", address: request.sessionWalletAddress },
|
|
114
|
-
to: { network: "ethereum:42161", address: request.sessionWalletAddress },
|
|
115
|
-
amount: "100000000", // $100 USDC (6 decimals)
|
|
116
|
-
fromToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC
|
|
117
|
-
// toToken omitted = native ETH (default behavior)
|
|
118
|
-
// slippage defaults to "0.5", priceImpact defaults to "0.5"
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (quote.success) {
|
|
122
|
-
console.log(`๐ฐ You'll receive: ${quote.data.assetReceive.amountFormatted}`);
|
|
123
|
-
console.log(`๐ธ Total fees: ${quote.data.fees.map(f => f.name).join(", ")}`);
|
|
124
|
-
} else if (quote.error) {
|
|
125
|
-
// Check for specific error types
|
|
126
|
-
if (quote.error === QUOTE_RESULT.WALLET_NOT_FOUND) {
|
|
127
|
-
console.log("๐ Wallet not found");
|
|
128
|
-
} else if (quote.error === QUOTE_RESULT.WALLET_MISMATCH) {
|
|
129
|
-
console.log("๐ Wallet address doesn't match session");
|
|
130
|
-
} else {
|
|
131
|
-
console.log("โ Quote not available for this swap");
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
131
|
+
## ๐ Unified Logging with agent.log()
|
|
135
132
|
|
|
136
|
-
|
|
133
|
+
Use `agent.log()` to communicate with your users and debug your agent. Every message appears in your terminal, and by default also shows up in the Circuit UI for your users to see. Simply pass `debug: true` to skip sending the message to the user.
|
|
137
134
|
|
|
138
135
|
```typescript
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
136
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
137
|
+
// Standard log: Shows to user in Circuit UI
|
|
138
|
+
await agent.log("Processing transaction");
|
|
139
|
+
|
|
140
|
+
// Error log: Shows to user in Circuit UI (as an error)
|
|
141
|
+
const result = await agent.memory.get("key");
|
|
142
|
+
if (!result.success) {
|
|
143
|
+
await agent.log(result.error_message || result.error, { error: true });
|
|
144
|
+
}
|
|
148
145
|
|
|
149
|
-
|
|
146
|
+
// Debug log: Only you see this in your terminal
|
|
147
|
+
await agent.log("Internal state: processing...", { debug: true });
|
|
150
148
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
// Retry with more permissive parameters
|
|
155
|
-
quoteRequest.priceImpact = "10.0";
|
|
156
|
-
quoteRequest.slippage = "10.0";
|
|
157
|
-
quote = await sdk.swidge.quote(quoteRequest);
|
|
158
|
-
}
|
|
149
|
+
// Logging objects
|
|
150
|
+
// Objects are pretty-printed to console and serialized/truncated for backend
|
|
151
|
+
await agent.log({ wallet: agent.sessionWalletAddress, status: "active" });
|
|
159
152
|
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const result = await sdk.swidge.execute(quote.data);
|
|
166
|
-
|
|
167
|
-
if (result.success && result.data) {
|
|
168
|
-
console.log(`๐ Status: ${result.data.status}`);
|
|
169
|
-
|
|
170
|
-
if (result.data.status === "success") {
|
|
171
|
-
console.log(`๐ค Sent: ${result.data.in.txs[0]}`);
|
|
172
|
-
console.log(`๐ฅ Received: ${result.data.out.txs[0]}`);
|
|
173
|
-
console.log("โ
Cross-chain swap completed!");
|
|
174
|
-
} else if (result.data.status === "failure") {
|
|
175
|
-
console.log("โ Transaction failed");
|
|
176
|
-
} else if (result.data.status === "refund") {
|
|
177
|
-
console.log("โฉ๏ธ Transaction was refunded");
|
|
178
|
-
} else if (result.data.status === "delayed") {
|
|
179
|
-
console.log("โฐ Transaction is delayed");
|
|
180
|
-
}
|
|
181
|
-
} else {
|
|
182
|
-
console.log(`โ Execute failed: ${result.error}`);
|
|
153
|
+
// Check if message reached the user
|
|
154
|
+
const logResult = await agent.log("Important message");
|
|
155
|
+
if (!logResult.success) {
|
|
156
|
+
// Rare - usually means Circuit UI is unreachable
|
|
183
157
|
}
|
|
184
|
-
} else {
|
|
185
|
-
console.log(`โ Quote failed after retry: ${quote.error}`);
|
|
186
|
-
return { success: false };
|
|
187
158
|
}
|
|
188
159
|
```
|
|
189
160
|
|
|
190
|
-
|
|
161
|
+
**What Your Users See:**
|
|
191
162
|
|
|
192
|
-
|
|
163
|
+
| Code | You See (Terminal) | User Sees (Circuit UI) |
|
|
164
|
+
|------|-------------------|----------------------|
|
|
165
|
+
| `agent.log("msg")` | โ
In your terminal | โ
In Circuit UI |
|
|
166
|
+
| `agent.log("msg", { error: true })` | โ
As error in terminal | โ
As error in Circuit UI |
|
|
167
|
+
| `agent.log("msg", { debug: true })` | โ
In terminal | โ Hidden from user |
|
|
168
|
+
| `agent.log("msg", { error: true, debug: true })` | โ
As error in terminal | โ Hidden from user |
|
|
193
169
|
|
|
194
|
-
|
|
170
|
+
## ๐พ Session Memory Storage
|
|
195
171
|
|
|
196
|
-
**
|
|
197
|
-
```typescript
|
|
198
|
-
{
|
|
199
|
-
from: { network: SwidgeNetwork, address: string },
|
|
200
|
-
to: { network: SwidgeNetwork, address: string },
|
|
201
|
-
amount: string, // Amount in token's smallest unit
|
|
202
|
-
fromToken?: string, // Source token contract (omit for native tokens)
|
|
203
|
-
toToken?: string, // Destination token contract (omit for native tokens)
|
|
204
|
-
slippage?: string, // Slippage tolerance % (default: "0.5")
|
|
205
|
-
priceImpact?: string // Max price impact % (default: "0.5")
|
|
206
|
-
}
|
|
207
|
-
```
|
|
172
|
+
Store and retrieve data for your agent's session with simple operations. Memory is **automatically scoped to your session ID**, and for now is simple string storage. You will need to handle serialization of whatever data you want to store here.
|
|
208
173
|
|
|
209
|
-
**Returns:**
|
|
210
174
|
```typescript
|
|
211
|
-
{
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
network: SwidgeNetwork,
|
|
217
|
-
address: string,
|
|
218
|
-
token: string | null,
|
|
219
|
-
amount: string,
|
|
220
|
-
amountFormatted: string,
|
|
221
|
-
amountUsd: string,
|
|
222
|
-
// ... additional fields
|
|
223
|
-
},
|
|
224
|
-
assetReceive: {
|
|
225
|
-
network: SwidgeNetwork,
|
|
226
|
-
address: string,
|
|
227
|
-
token: string | null,
|
|
228
|
-
amount: string,
|
|
229
|
-
amountFormatted: string,
|
|
230
|
-
amountUsd: string,
|
|
231
|
-
// ... additional fields
|
|
232
|
-
},
|
|
233
|
-
priceImpact: {
|
|
234
|
-
usd?: string,
|
|
235
|
-
percentage?: string
|
|
236
|
-
},
|
|
237
|
-
fees: Array<{
|
|
238
|
-
name: string,
|
|
239
|
-
amount?: string,
|
|
240
|
-
amountFormatted?: string,
|
|
241
|
-
amountUsd?: string
|
|
242
|
-
}>,
|
|
243
|
-
steps: Array<SwidgeUnsignedStep> // Transaction steps to execute
|
|
244
|
-
},
|
|
245
|
-
error?: string,
|
|
246
|
-
errorDetails?: {
|
|
247
|
-
message: string,
|
|
248
|
-
status?: number,
|
|
249
|
-
statusText?: string
|
|
175
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
176
|
+
// Set a value
|
|
177
|
+
const result = await agent.memory.set("lastSwapNetwork", "ethereum:42161");
|
|
178
|
+
if (!result.success) {
|
|
179
|
+
await agent.log(result.error_message || result.error, { error: true });
|
|
250
180
|
}
|
|
181
|
+
|
|
182
|
+
// Get a value
|
|
183
|
+
const getResult = await agent.memory.get("lastSwapNetwork");
|
|
184
|
+
if (getResult.success && getResult.data) {
|
|
185
|
+
const network = getResult.data.value;
|
|
186
|
+
await agent.log(`Using network: ${network}`);
|
|
187
|
+
} else {
|
|
188
|
+
await agent.log("No saved network found");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// List all keys
|
|
192
|
+
const listResult = await agent.memory.list();
|
|
193
|
+
if (listResult.success && listResult.data) {
|
|
194
|
+
await agent.log(`Found ${listResult.data.count} keys: ${listResult.data.keys}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Delete a key
|
|
198
|
+
await agent.memory.delete("tempData");
|
|
251
199
|
}
|
|
252
200
|
```
|
|
253
201
|
|
|
254
|
-
**
|
|
255
|
-
|
|
256
|
-
The SDK exports a `QUOTE_RESULT` constant that provides type-safe error checking with full IntelliSense support:
|
|
202
|
+
**All memory operations return responses with `.success` and `.error_message`:**
|
|
257
203
|
|
|
258
204
|
```typescript
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (quote.error === QUOTE_RESULT.WALLET_NOT_FOUND) {
|
|
263
|
-
// Handle wallet not found
|
|
264
|
-
} else if (quote.error === QUOTE_RESULT.WALLET_MISMATCH) {
|
|
265
|
-
// Handle wallet mismatch
|
|
205
|
+
const result = await agent.memory.set("key", "value");
|
|
206
|
+
if (!result.success) {
|
|
207
|
+
await agent.log(`Failed to save: ${result.error_message}`, { error: true });
|
|
266
208
|
}
|
|
267
209
|
```
|
|
268
210
|
|
|
269
|
-
|
|
270
|
-
- `QUOTE_RESULT.FOUND` - Success case (not an error)
|
|
271
|
-
- `QUOTE_RESULT.NO_QUOTE_PROVIDED` - Generic API error
|
|
272
|
-
- `QUOTE_RESULT.WALLET_NOT_FOUND` - Wallet address not found
|
|
273
|
-
- `QUOTE_RESULT.WALLET_MISMATCH` - From wallet does not match session wallet
|
|
274
|
-
|
|
275
|
-
#### `sdk.swidge.execute(quoteData: SwidgeExecuteRequest): Promise<SwidgeExecuteResponse>`
|
|
211
|
+
## ๐ Cross-Chain Swaps with Swidge
|
|
276
212
|
|
|
277
|
-
|
|
213
|
+
Built-in Swidge integration for seamless cross-chain token swaps and bridges.
|
|
278
214
|
|
|
279
|
-
|
|
215
|
+
### Get and execute a quote
|
|
216
|
+
> Note: It is important to always validate quotes before executing. Circuit will always do its best to return a quote, and as of now, will only filter out quotes with price impacts exceeding 100% to ensure maximum flexibility. It is on the agent to makes sure a quote is valid, given its own parameters
|
|
280
217
|
|
|
281
|
-
**Parameters:**
|
|
282
218
|
```typescript
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
219
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
220
|
+
// 1. Get quote
|
|
221
|
+
const quote = await agent.swidge.quote({
|
|
222
|
+
from: { network: "ethereum:8453", address: agent.sessionWalletAddress },
|
|
223
|
+
to: { network: "ethereum:137", address: agent.sessionWalletAddress },
|
|
224
|
+
amount: "1000000000000000", // 0.001 ETH
|
|
225
|
+
toToken: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
|
|
226
|
+
slippage: "2.0",
|
|
227
|
+
priceImpact: "100.0"
|
|
228
|
+
});
|
|
293
229
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
230
|
+
if (!quote.success) {
|
|
231
|
+
await agent.log(`Quote failed: ${quote.error_message}`, { error: true });
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (quote.data && Math.abs(parseFloat(quote.data.priceImpact.percentage)) > 10) {
|
|
236
|
+
await agent.log(`Warning: Price impact is too high: ${quote.data.priceImpact.percentage}%`, { error: true, debug: true });
|
|
237
|
+
} else if (quote.data) {
|
|
238
|
+
await agent.log(`You'll receive: ${quote.data.assetReceive.amountFormatted}`);
|
|
239
|
+
await agent.log(`Fees: ${quote.data.fees.map(f => f.name).join(', ')}`);
|
|
240
|
+
|
|
241
|
+
// 2. Execute the swap
|
|
242
|
+
const result = await agent.swidge.execute(quote.data);
|
|
243
|
+
|
|
244
|
+
if (result.success && result.data) {
|
|
245
|
+
await agent.log(`Swap status: ${result.data.status}`);
|
|
246
|
+
if (result.data.status === "success") {
|
|
247
|
+
await agent.log("โ
Swap completed!");
|
|
248
|
+
await agent.log(`In tx: ${result.data.in.txs}`);
|
|
249
|
+
await agent.log(`Out tx: ${result.data.out.txs}`);
|
|
250
|
+
} else if (result.data.status === "failure") {
|
|
251
|
+
await agent.log("โ Swap failed", { error: true });
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
await agent.log(result.error_message || result.error, { error: true });
|
|
255
|
+
}
|
|
315
256
|
}
|
|
316
257
|
}
|
|
317
258
|
```
|
|
318
259
|
|
|
319
|
-
**Execution Flow:**
|
|
320
|
-
1. SDK signs and broadcasts transactions from the `steps` array
|
|
321
|
-
2. Monitors cross-chain execution progress
|
|
322
|
-
3. Returns final status once complete (never returns "pending" or "waiting")
|
|
323
|
-
|
|
324
|
-
**Status Meanings:**
|
|
325
|
-
- `"success"` - All transactions completed successfully
|
|
326
|
-
- `"failure"` - One or more transactions failed
|
|
327
|
-
- `"refund"` - Transaction was refunded (e.g., due to timeout or failure)
|
|
328
|
-
- `"delayed"` - Transaction is taking longer than expected but still processing
|
|
329
|
-
|
|
330
|
-
|
|
331
260
|
## ๐ Polymarket Prediction Markets
|
|
332
261
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
### 4. Polymarket Trading
|
|
262
|
+
Trade prediction markets on Polygon.
|
|
336
263
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
Fetch all current positions for the session wallet:
|
|
264
|
+
### Place Market Orders
|
|
265
|
+
> Note: Right now, polymarket's API accepts different decimal precision for buys and sells, this will result in dust positions if you are selling out of a position before expiry. Once expired, dust can be cleaned up during the redeem step.
|
|
340
266
|
|
|
341
267
|
```typescript
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
268
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
269
|
+
// Buy order - size is USD amount to spend
|
|
270
|
+
const buyOrder = await agent.platforms.polymarket.marketOrder({
|
|
271
|
+
tokenId: "86192057611122246511563653509192966169513312957180910360241289053249649036697",
|
|
272
|
+
size: 3, // Spend $3
|
|
273
|
+
side: "BUY"
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
if (buyOrder.success && buyOrder.data) {
|
|
277
|
+
await agent.log(`Order ID: ${buyOrder.data.orderInfo.orderId}`);
|
|
278
|
+
await agent.log(`Price: $${buyOrder.data.orderInfo.priceUsd}`);
|
|
279
|
+
await agent.log(`Total: $${buyOrder.data.orderInfo.totalPriceUsd}`);
|
|
280
|
+
} else {
|
|
281
|
+
await agent.log(buyOrder.error_message || buyOrder.error, { error: true });
|
|
352
282
|
}
|
|
283
|
+
|
|
284
|
+
// Sell order - size is number of shares to sell
|
|
285
|
+
const sellOrder = await agent.platforms.polymarket.marketOrder({
|
|
286
|
+
tokenId: "86192057611122246511563653509192966169513312957180910360241289053249649036697",
|
|
287
|
+
size: 5.5, // Sell 5.5 shares
|
|
288
|
+
side: "SELL"
|
|
289
|
+
});
|
|
353
290
|
}
|
|
354
291
|
```
|
|
355
292
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
Execute buy or sell market orders:
|
|
293
|
+
### Redeem Positions
|
|
359
294
|
|
|
360
295
|
```typescript
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
size: 10, // Spend $10 to buy shares
|
|
365
|
-
side: "BUY"
|
|
366
|
-
});
|
|
296
|
+
async function stop(agent: AgentContext): Promise<void> {
|
|
297
|
+
/**Redeem all settled positions.*/
|
|
298
|
+
const redemption = await agent.platforms.polymarket.redeemPositions();
|
|
367
299
|
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if (buyOrder.success && buyOrder.data) {
|
|
376
|
-
console.log(`Order Success: ${buyOrder.data.success}`);
|
|
377
|
-
console.log(`Order ID: ${buyOrder.data.orderInfo.orderId}`);
|
|
378
|
-
console.log(`Filled at $${buyOrder.data.orderInfo.priceUsd} per share`);
|
|
379
|
-
console.log(`Total cost: $${buyOrder.data.orderInfo.totalPriceUsd}`);
|
|
300
|
+
if (redemption.success && redemption.data) {
|
|
301
|
+
const successful = redemption.data.filter(r => r.success);
|
|
302
|
+
await agent.log(`Redeemed ${successful.length} positions`);
|
|
303
|
+
} else {
|
|
304
|
+
await agent.log(redemption.error_message || redemption.error, { error: true });
|
|
305
|
+
}
|
|
380
306
|
}
|
|
381
307
|
```
|
|
382
308
|
|
|
383
|
-
|
|
309
|
+
## ๐ Sign & Send Transactions
|
|
384
310
|
|
|
385
|
-
|
|
311
|
+
### Ethereum (any EVM chain)
|
|
386
312
|
|
|
387
313
|
```typescript
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
314
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
315
|
+
// Self-send demo - send a small amount to yourself
|
|
316
|
+
const response = await agent.signAndSend({
|
|
317
|
+
network: "ethereum:1",
|
|
318
|
+
request: {
|
|
319
|
+
toAddress: agent.sessionWalletAddress, // Send to self
|
|
320
|
+
data: "0x",
|
|
321
|
+
value: "100000000000000" // 0.0001 ETH
|
|
322
|
+
},
|
|
323
|
+
message: "Self-send demo"
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (response.success) {
|
|
327
|
+
await agent.log(`Transaction sent: ${response.txHash}`);
|
|
328
|
+
if (response.transactionUrl) {
|
|
329
|
+
await agent.log(`View: ${response.transactionUrl}`);
|
|
396
330
|
}
|
|
331
|
+
} else {
|
|
332
|
+
await agent.log(response.error_message || response.error, { error: true });
|
|
397
333
|
}
|
|
398
334
|
}
|
|
399
|
-
|
|
400
|
-
// Redeem specific positions by token IDs
|
|
401
|
-
const specificRedemption = await sdk.polymarket.redeemPositions({
|
|
402
|
-
tokenIds: ["123456", "789012"]
|
|
403
|
-
});
|
|
404
335
|
```
|
|
405
336
|
|
|
406
|
-
###
|
|
407
|
-
|
|
408
|
-
#### `sdk.polymarket.positions(): Promise<PolymarketPositionsResponse>`
|
|
409
|
-
|
|
410
|
-
Get all current positions for the session wallet.
|
|
337
|
+
### Solana
|
|
411
338
|
|
|
412
|
-
**Returns:**
|
|
413
339
|
```typescript
|
|
414
|
-
{
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
averagePriceUsd: string,
|
|
428
|
-
pnlUsd: string,
|
|
429
|
-
pnlPercent: string,
|
|
430
|
-
isRedeemable: boolean,
|
|
431
|
-
endDate: string,
|
|
432
|
-
// ... additional fields
|
|
433
|
-
}>
|
|
434
|
-
},
|
|
435
|
-
error?: string,
|
|
436
|
-
errorDetails?: { message: string, status?: number, statusText?: string }
|
|
340
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
341
|
+
const response = await agent.signAndSend({
|
|
342
|
+
network: "solana",
|
|
343
|
+
request: {
|
|
344
|
+
hexTransaction: "010001030a0b..." // serialized VersionedTransaction
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
if (response.success) {
|
|
349
|
+
await agent.log(`Transaction: ${response.txHash}`);
|
|
350
|
+
} else {
|
|
351
|
+
await agent.log(response.error_message || response.error, { error: true });
|
|
352
|
+
}
|
|
437
353
|
}
|
|
438
354
|
```
|
|
439
355
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
Place a buy or sell market order.
|
|
356
|
+
## โก Error Handling
|
|
443
357
|
|
|
444
|
-
|
|
445
|
-
- **BUY**: `size` is the USD amount to spend (e.g., 10 = $10 worth of shares)
|
|
446
|
-
- **SELL**: `size` is the number of shares/tokens to sell (e.g., 10 = 10 shares)
|
|
358
|
+
**All SDK methods return response objects with `.success` and `.error_message`:**
|
|
447
359
|
|
|
448
|
-
**Parameters:**
|
|
449
|
-
```typescript
|
|
450
|
-
{
|
|
451
|
-
tokenId: string, // Market token ID for the position
|
|
452
|
-
size: number, // For BUY: USD amount. For SELL: Number of shares
|
|
453
|
-
side: "BUY" | "SELL"
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
360
|
|
|
457
|
-
**
|
|
458
|
-
```typescript
|
|
459
|
-
{
|
|
460
|
-
success: boolean,
|
|
461
|
-
data?: {
|
|
462
|
-
success: boolean, // Whether the order was successfully submitted
|
|
463
|
-
orderInfo: {
|
|
464
|
-
orderId: string,
|
|
465
|
-
side: string,
|
|
466
|
-
size: string,
|
|
467
|
-
priceUsd: string,
|
|
468
|
-
totalPriceUsd: string,
|
|
469
|
-
txHashes: string[]
|
|
470
|
-
}
|
|
471
|
-
},
|
|
472
|
-
error?: string,
|
|
473
|
-
errorDetails?: { message: string, status?: number, statusText?: string }
|
|
474
|
-
}
|
|
475
|
-
```
|
|
361
|
+
**Uncaught Exceptions:**
|
|
476
362
|
|
|
477
|
-
|
|
363
|
+
If your function throws an uncaught exception, the Agent SDK automatically:
|
|
364
|
+
1. Logs the error to console (visible in local dev and cloud logs)
|
|
365
|
+
2. Updates the job in the circuit backend with status='failed' and logs the error.
|
|
478
366
|
|
|
479
|
-
Redeem settled positions to claim winnings.
|
|
480
|
-
|
|
481
|
-
**Parameters (optional):**
|
|
482
367
|
```typescript
|
|
483
|
-
{
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
```
|
|
368
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
369
|
+
// This typo will be caught and logged automatically
|
|
370
|
+
await agent.memmory.set("key", "value"); // TypeError
|
|
487
371
|
|
|
488
|
-
|
|
489
|
-
```typescript
|
|
490
|
-
{
|
|
491
|
-
success: boolean,
|
|
492
|
-
data?: Array<{
|
|
493
|
-
success: boolean,
|
|
494
|
-
position: { /* Full position details */ },
|
|
495
|
-
transactionHash: string | null
|
|
496
|
-
}>,
|
|
497
|
-
error?: string,
|
|
498
|
-
errorDetails?: { message: string, status?: number, statusText?: string }
|
|
372
|
+
// No need for try/catch around everything!
|
|
499
373
|
}
|
|
500
374
|
```
|
|
501
375
|
|
|
502
|
-
|
|
376
|
+
## ๐ ๏ธ Deployment
|
|
503
377
|
|
|
504
|
-
|
|
505
|
-
const executionFunction: ExecutionFunctionContract = async (request) => {
|
|
506
|
-
const sdk = new AgentSdk({ sessionId: request.sessionId });
|
|
507
|
-
|
|
508
|
-
try {
|
|
509
|
-
// Get current positions
|
|
510
|
-
const positions = await sdk.polymarket.positions();
|
|
511
|
-
|
|
512
|
-
if (!positions.success || !positions.data) {
|
|
513
|
-
throw new Error(`Failed to get positions: ${positions.error}`);
|
|
514
|
-
}
|
|
378
|
+
### What You Write
|
|
515
379
|
|
|
516
|
-
|
|
517
|
-
const targetPosition = positions.data.positions.find(
|
|
518
|
-
p => p.tokenId === "YOUR_TOKEN_ID"
|
|
519
|
-
);
|
|
520
|
-
|
|
521
|
-
if (targetPosition && Number(targetPosition.formattedShares) > 0) {
|
|
522
|
-
// Sell position
|
|
523
|
-
const sellOrder = await sdk.polymarket.marketOrder({
|
|
524
|
-
tokenId: targetPosition.tokenId,
|
|
525
|
-
size: Number(targetPosition.formattedShares),
|
|
526
|
-
side: "SELL"
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
if (sellOrder.success && sellOrder.data?.orderInfo) {
|
|
530
|
-
await sdk.sendLog({
|
|
531
|
-
type: "observe",
|
|
532
|
-
shortMessage: `Sold ${targetPosition.outcome} for $${sellOrder.data.orderInfo.totalPriceUsd}`
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
380
|
+
Your agent code should look like this - just define your functions and add the boilerplate at the bottom:
|
|
536
381
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
await sdk.sendLog({
|
|
540
|
-
type: "error",
|
|
541
|
-
shortMessage: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
542
|
-
});
|
|
543
|
-
return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
|
|
544
|
-
}
|
|
545
|
-
};
|
|
382
|
+
```typescript
|
|
383
|
+
import { Agent, AgentContext } from "@circuitorg/agent-sdk";
|
|
546
384
|
|
|
547
|
-
|
|
548
|
-
|
|
385
|
+
async function run(agent: AgentContext): Promise<void> {
|
|
386
|
+
await agent.log("Hello from my agent!");
|
|
387
|
+
// Your agent logic here
|
|
388
|
+
}
|
|
549
389
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
390
|
+
async function stop(agent: AgentContext): Promise<void> {
|
|
391
|
+
await agent.log("Cleaning up...");
|
|
392
|
+
// Optional cleanup logic
|
|
393
|
+
}
|
|
553
394
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
395
|
+
// ============================================================================
|
|
396
|
+
// BOILERPLATE - Don't modify, this handles local testing AND Circuit deployment
|
|
397
|
+
// ============================================================================
|
|
398
|
+
const agent = new Agent({
|
|
399
|
+
runFunction: run,
|
|
400
|
+
stopFunction: stop
|
|
401
|
+
});
|
|
561
402
|
|
|
562
|
-
|
|
563
|
-
} catch (error) {
|
|
564
|
-
return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
|
|
565
|
-
}
|
|
566
|
-
};
|
|
403
|
+
export default agent.getExport();
|
|
567
404
|
```
|
|
568
405
|
|
|
406
|
+
That's it! The boilerplate code automatically handles the rest
|
|
569
407
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
### Barebones Agent
|
|
573
|
-
>This is the wireframe structure all agents need to have (executionFunction, stopFunction, and export default agent.getWorkerExport()). The main thing an agent dev needs to focus on is the execution logic, and any cleanup logic that would need to be ran whenever a user decides to stop an agent session.
|
|
574
|
-
|
|
575
|
-
```typescript
|
|
576
|
-
import { Agent, AgentSdk, ExecutionFunctionContract, StopFunctionContract } from "@circuitorg/agent-sdk";
|
|
577
|
-
|
|
578
|
-
// Agent execution function using the new AgentSdk v1.0 API
|
|
579
|
-
const executionFunction: ExecutionFunctionContract = async (request) => {
|
|
580
|
-
console.log(`๐ Agent execution called for session ${request.sessionId}`);
|
|
581
|
-
|
|
582
|
-
const sdk = new AgentSdk({
|
|
583
|
-
sessionId: request.sessionId,
|
|
584
|
-
});
|
|
408
|
+
### Deploy to Circuit
|
|
585
409
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
await sdk.sendLog({
|
|
591
|
-
type: "observe",
|
|
592
|
-
shortMessage: `๐ Agent SDK v1.0 demo - Hello ${userAddress}`
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
return { success: true};
|
|
596
|
-
} catch (error) {
|
|
597
|
-
await sdk.sendLog({
|
|
598
|
-
type: "error",
|
|
599
|
-
shortMessage: `Agent execution error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
600
|
-
});
|
|
601
|
-
return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
|
|
602
|
-
}
|
|
603
|
-
};
|
|
410
|
+
```bash
|
|
411
|
+
circuit publish
|
|
412
|
+
```
|
|
413
|
+
> See the circuit cli docs for more details
|
|
604
414
|
|
|
605
|
-
const stopFunction: StopFunctionContract = async (request) => {
|
|
606
|
-
const sdk = new AgentSdk({
|
|
607
|
-
sessionId: request.sessionId,
|
|
608
|
-
testing: true
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
try {
|
|
612
|
-
await sdk.sendLog({
|
|
613
|
-
type: "observe",
|
|
614
|
-
shortMessage: "๐ Agent stopping with new SDK v1.0"
|
|
615
|
-
});
|
|
616
|
-
return { success: true };
|
|
617
|
-
} catch (error) {
|
|
618
|
-
return { success: false, error: error instanceof Error ? error.message : "Unknown error" };
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
415
|
|
|
622
|
-
|
|
623
|
-
const agent = new Agent({ executionFunction, stopFunction });
|
|
416
|
+
### Test Locally
|
|
624
417
|
|
|
625
|
-
|
|
626
|
-
export default agent.getWorkerExport();
|
|
418
|
+
## ๐งช Manual Instantiation (Testing)
|
|
627
419
|
|
|
628
|
-
|
|
420
|
+
For testing in scripts or Node.js REPL, you can manually create an `AgentContext`:
|
|
629
421
|
|
|
630
|
-
|
|
631
|
-
### Sample self sender
|
|
632
|
-
>This demonstrates a very basic structure of fetching balances and sending 1% of that amount to yourself. It highlights what stage in the transaction process you will "hand off" the transaction to the circuit infrastructure for signing and sending (broadcasting).
|
|
633
422
|
```typescript
|
|
634
|
-
import {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
423
|
+
import { AgentContext } from "@circuitorg/agent-sdk";
|
|
424
|
+
|
|
425
|
+
// Create agent context with test data
|
|
426
|
+
// Tip: Get a real session ID and wallet from running 'circuit run -m local -x execute'
|
|
427
|
+
const SESSION_ID = 123; // <your-session-id>
|
|
428
|
+
const WALLET_ADDRESS = "your-wallet-address";
|
|
429
|
+
|
|
430
|
+
// Create sample position data - helpful for testing your agents treatment of different scenarios
|
|
431
|
+
// for example if you want to test some re-balancing logic, you can build a sample set of positions here
|
|
432
|
+
// In production, or when using the cli, these will be live values for the session
|
|
433
|
+
const SAMPLE_POSITION = {
|
|
434
|
+
network: "ethereum:137",
|
|
435
|
+
assetAddress: "0x4d97dcd97ec945f40cf65f87097ace5ea0476045",
|
|
436
|
+
tokenId: "86192057611122246511563653509192966169513312957180910360241289053249649036697",
|
|
437
|
+
avgUnitCost: "0.779812797920499100",
|
|
438
|
+
currentQty: "41282044"
|
|
649
439
|
};
|
|
650
440
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
})
|
|
656
|
-
// balanceOf(address) - ERC-20 standard
|
|
657
|
-
const data = `0x70a08231${params.address
|
|
658
|
-
.replace(/^0x/, "")
|
|
659
|
-
.padStart(64, "0")}`;
|
|
660
|
-
|
|
661
|
-
const balanceCall = {
|
|
662
|
-
jsonrpc: "2.0",
|
|
663
|
-
id: 1,
|
|
664
|
-
method: "eth_call",
|
|
665
|
-
params: [{ to: params.token, data }, "latest"],
|
|
666
|
-
} as const;
|
|
667
|
-
|
|
668
|
-
const balanceRes = await fetch(params.rpcUrl, {
|
|
669
|
-
method: "POST",
|
|
670
|
-
headers: { "content-type": "application/json" },
|
|
671
|
-
body: JSON.stringify(balanceCall),
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
const balanceJson = (await balanceRes.json()) as { result?: string };
|
|
675
|
-
const balHex = balanceJson.result || "0x0";
|
|
676
|
-
const amount = BigInt(balHex);
|
|
677
|
-
|
|
678
|
-
// decimals() - ERC-20 standard
|
|
679
|
-
const decimalsCall = {
|
|
680
|
-
jsonrpc: "2.0",
|
|
681
|
-
id: 2,
|
|
682
|
-
method: "eth_call",
|
|
683
|
-
params: [{ to: params.token, data: "0x313ce567" }, "latest"],
|
|
684
|
-
} as const;
|
|
685
|
-
|
|
686
|
-
const decimalsRes = await fetch(params.rpcUrl, {
|
|
687
|
-
method: "POST",
|
|
688
|
-
headers: { "content-type": "application/json" },
|
|
689
|
-
body: JSON.stringify(decimalsCall),
|
|
690
|
-
});
|
|
441
|
+
const agent = new AgentContext({
|
|
442
|
+
sessionId: SESSION_ID,
|
|
443
|
+
sessionWalletAddress: WALLET_ADDRESS,
|
|
444
|
+
currentPositions: [SAMPLE_POSITION]
|
|
445
|
+
});
|
|
691
446
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
: 18;
|
|
447
|
+
// Use it just like in production!
|
|
448
|
+
await agent.log("Testing agent functionality!");
|
|
449
|
+
await agent.memory.set("test_key", "test_value");
|
|
696
450
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
isToken: true,
|
|
701
|
-
};
|
|
451
|
+
const result = await agent.memory.get("test_key");
|
|
452
|
+
if (result.success && result.data) {
|
|
453
|
+
console.log(`Value: ${result.data.value}`);
|
|
702
454
|
}
|
|
455
|
+
```
|
|
703
456
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
"1": "โ",
|
|
708
|
-
"2": "โ",
|
|
709
|
-
"3": "โ",
|
|
710
|
-
"4": "โ",
|
|
711
|
-
"5": "โ
",
|
|
712
|
-
"6": "โ",
|
|
713
|
-
"7": "โ",
|
|
714
|
-
"8": "โ",
|
|
715
|
-
"9": "โ",
|
|
716
|
-
};
|
|
717
|
-
return String(num)
|
|
718
|
-
.split("")
|
|
719
|
-
.map((c) => map[c] ?? c)
|
|
720
|
-
.join("");
|
|
721
|
-
};
|
|
457
|
+
**Key Points:**
|
|
458
|
+
- Use real session IDs from the CLI for proper testing
|
|
459
|
+
- All SDK methods work the same way as in production
|
|
722
460
|
|
|
723
|
-
|
|
724
|
-
amount: bigint,
|
|
725
|
-
decimals: number,
|
|
726
|
-
subscriptDecimals: number = 4,
|
|
727
|
-
standardDecimals: number = 6
|
|
728
|
-
): string => {
|
|
729
|
-
const sign = amount < 0n ? "-" : "";
|
|
730
|
-
const s = (amount < 0n ? -amount : amount).toString();
|
|
731
|
-
|
|
732
|
-
const hasFraction = decimals > 0;
|
|
733
|
-
const integerPart =
|
|
734
|
-
s.length > decimals ? s.slice(0, s.length - decimals) : "0";
|
|
735
|
-
const fractional = hasFraction
|
|
736
|
-
? s.length > decimals
|
|
737
|
-
? s.slice(s.length - decimals)
|
|
738
|
-
: s.padStart(decimals, "0")
|
|
739
|
-
: "";
|
|
740
|
-
|
|
741
|
-
const groupedInt = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
742
|
-
|
|
743
|
-
if (!hasFraction) return sign + groupedInt;
|
|
744
|
-
if (/^0+$/.test(fractional)) return sign + groupedInt;
|
|
745
|
-
|
|
746
|
-
const leadingZeros = fractional.match(/^0+/)?.[0].length ?? 0;
|
|
747
|
-
|
|
748
|
-
if (leadingZeros >= 3) {
|
|
749
|
-
// Use subscript notation for numbers with 3+ leading zeros
|
|
750
|
-
const rest = fractional.slice(leadingZeros);
|
|
751
|
-
const truncatedRest = rest.slice(0, subscriptDecimals);
|
|
752
|
-
const compressedFrac = `0${toSubscript(leadingZeros)}${truncatedRest}`;
|
|
753
|
-
return `${sign}${groupedInt}.${compressedFrac}`;
|
|
754
|
-
}
|
|
461
|
+
## ๐ Working Examples
|
|
755
462
|
|
|
756
|
-
|
|
757
|
-
const truncatedFrac = fractional.slice(0, standardDecimals);
|
|
758
|
-
const trimmedFrac = truncatedFrac.replace(/0+$/, ""); // Remove trailing zeros
|
|
463
|
+
The SDK includes complete working examples to help you get started:
|
|
759
464
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
465
|
+
### [`demo-agent.ts`](./examples/demo-agent.ts)
|
|
466
|
+
An agent demonstrating all main features of the sdk:
|
|
467
|
+
- Memory operations (set, get, list, delete)
|
|
468
|
+
- Polymarket integration (market orders, position redemption)
|
|
469
|
+
- Swidge
|
|
470
|
+
- Self sending using sign and send
|
|
471
|
+
- Unified logging patterns
|
|
763
472
|
|
|
764
|
-
|
|
765
|
-
const executionFunction: ExecutionFunctionContract = async (request) => {
|
|
766
|
-
console.log(`๐ Agent execution called for session ${request.sessionId}`);
|
|
473
|
+
This is a great starting point for building your own agent.
|
|
767
474
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
"๐ Starting Agent SDK v1.0 comprehensive demo -- live test",
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
const PEPE_ARB = "0x25d887Ce7a35172C62FeBFD67a1856F20FaEbB00" as `0x${string}`;
|
|
780
|
-
const pepeBalance = await getEvmTokenBalance({
|
|
781
|
-
rpcUrl: "https://arb-mainnet.g.alchemy.com/v2/<YOUR_API_KEY>",
|
|
782
|
-
address: request.sessionWalletAddress,
|
|
783
|
-
token: PEPE_ARB,
|
|
784
|
-
});
|
|
785
|
-
|
|
786
|
-
const pepeBalanceFormatted = formatTokenAmount(
|
|
787
|
-
BigInt(pepeBalance.amount),
|
|
788
|
-
pepeBalance.decimals
|
|
789
|
-
);
|
|
790
|
-
|
|
791
|
-
await sdk.sendLog({
|
|
792
|
-
type: "observe",
|
|
793
|
-
shortMessage: `PEPE Balance (Arbitrum): ${pepeBalanceFormatted} PEPE`,
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
const abi = parseAbi([
|
|
798
|
-
"function transfer(address to, uint256 value) returns (bool)",
|
|
799
|
-
]);
|
|
800
|
-
const demoAmount = BigInt(pepeBalance.amount) / 100n;
|
|
801
|
-
|
|
802
|
-
const data = encodeFunctionData({
|
|
803
|
-
abi,
|
|
804
|
-
functionName: "transfer",
|
|
805
|
-
args: [request.sessionWalletAddress as `0x${string}`, demoAmount],
|
|
806
|
-
});
|
|
807
|
-
|
|
808
|
-
// Demo custom contract call
|
|
809
|
-
await sdk.signAndSend({
|
|
810
|
-
network: "ethereum:42161",
|
|
811
|
-
request: {
|
|
812
|
-
toAddress: PEPE_ARB,
|
|
813
|
-
data: data,
|
|
814
|
-
value: "0", // No ETH value for token transfers
|
|
815
|
-
},
|
|
816
|
-
message: "Custom PEPE transfer demo",
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
await sdk.sendLog({
|
|
821
|
-
type: "observe",
|
|
822
|
-
shortMessage:
|
|
823
|
-
"โ
Agent SDK v1.0 comprehensive demo completed successfully!",
|
|
824
|
-
});
|
|
825
|
-
|
|
826
|
-
return { success: true };
|
|
827
|
-
} catch (error) {
|
|
828
|
-
console.error("๐ฅ Agent execution error:", error);
|
|
829
|
-
|
|
830
|
-
await sdk.sendLog({
|
|
831
|
-
type: "error",
|
|
832
|
-
shortMessage: `Agent execution error: ${
|
|
833
|
-
error instanceof Error ? error.message : "Unknown error"
|
|
834
|
-
}`,
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
// Optional - Return detailed error information for the CLI to display
|
|
838
|
-
return {
|
|
839
|
-
success: false,
|
|
840
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
841
|
-
errorDetails: {
|
|
842
|
-
name: error instanceof Error ? error.name : "Unknown",
|
|
843
|
-
message: error instanceof Error ? error.message : String(error),
|
|
844
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
845
|
-
timestamp: new Date().toISOString(),
|
|
846
|
-
sessionId: request.sessionId,
|
|
847
|
-
agentSlug: (globalThis as any).CIRCUIT_AGENT_SLUG || "unknown",
|
|
848
|
-
errorType: "AGENT_EXECUTION_ERROR",
|
|
849
|
-
},
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
};
|
|
475
|
+
### [`examples/kitchen-sink.ts`](./examples/kitchen-sink.ts)
|
|
476
|
+
A comprehensive script showing all SDK features:
|
|
477
|
+
- Manual AgentContext instantiation for testing
|
|
478
|
+
- All memory operations with examples
|
|
479
|
+
- Cross-chain swaps with Swidge (quotes, execution, price impact checks)
|
|
480
|
+
- Polymarket operations (buy/sell orders, redemptions)
|
|
481
|
+
- Custom transactions with signAndSend
|
|
482
|
+
- Logging patterns (standard, error, debug)
|
|
853
483
|
|
|
854
|
-
|
|
855
|
-
const sdk = new AgentSdk({
|
|
856
|
-
sessionId: request.sessionId,
|
|
857
|
-
testing: true,
|
|
858
|
-
});
|
|
484
|
+
Run this script to experiment with the SDK before deploying your agent.
|
|
859
485
|
|
|
860
|
-
|
|
861
|
-
await sdk.sendLog({
|
|
862
|
-
type: "observe",
|
|
863
|
-
shortMessage: "๐ Agent stopping with new SDK v1.0",
|
|
864
|
-
});
|
|
865
|
-
return { success: true };
|
|
866
|
-
} catch (error) {
|
|
867
|
-
console.error("๐ฅ Agent stop error:", error);
|
|
868
|
-
|
|
869
|
-
return {
|
|
870
|
-
success: false,
|
|
871
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
872
|
-
errorDetails: {
|
|
873
|
-
name: error instanceof Error ? error.name : "Unknown",
|
|
874
|
-
message: error instanceof Error ? error.message : String(error),
|
|
875
|
-
stack: error instanceof Error ? error.stack : undefined,
|
|
876
|
-
timestamp: new Date().toISOString(),
|
|
877
|
-
sessionId: request.sessionId,
|
|
878
|
-
agentSlug: (globalThis as any).CIRCUIT_AGENT_SLUG || "unknown",
|
|
879
|
-
},
|
|
880
|
-
};
|
|
881
|
-
}
|
|
882
|
-
};
|
|
486
|
+
## ๐ฏ Key Takeaways
|
|
883
487
|
|
|
884
|
-
|
|
885
|
-
|
|
488
|
+
1. **Single Interface**: Everything you need is in the `AgentContext` object
|
|
489
|
+
2. **No Return Values**: Functions return `void` - uncaught errors are handled automatically by circuit
|
|
490
|
+
3. **Unified Logging**: `agent.log()` with simple `debug` and `error` flags
|
|
491
|
+
4. **Check `.success`**: All SDK methods return response objects with `.success` and `.error_message`
|
|
886
492
|
|
|
887
|
-
|
|
888
|
-
export default agent.getWorkerExport();
|
|
493
|
+
## ๐ Additional Resources
|
|
889
494
|
|
|
890
|
-
|
|
495
|
+
- [Circuit CLI](https://github.com/circuitorg/agents-cli) - Deploy and manage agents
|
|
496
|
+
- [`demo-agent.ts`](./examples/demo-agent.ts) - Production-ready agent template
|
|
497
|
+
- [`examples/kitchen-sink.ts`](./examples/kitchen-sink.ts) - Comprehensive SDK demo
|