@sherwoodagent/cli 0.73.3 → 0.74.2
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/{abis-KP7M4IPM.js → abis-D7EDCKVZ.js} +2 -2
- package/dist/{addresses-DGB3D457.js → addresses-OFFTM6NG.js} +4 -4
- package/dist/{agent-UNSW5IPO.js → agent-IJOOONON.js} +8 -8
- package/dist/{chat-KT2WVDT2.js → chat-3PM3GP3C.js} +9 -10
- package/dist/{chat-KT2WVDT2.js.map → chat-3PM3GP3C.js.map} +1 -1
- package/dist/{chunk-ROKE6ZAW.js → chunk-2OHX3O55.js} +6 -20
- package/dist/chunk-2OHX3O55.js.map +1 -0
- package/dist/{chunk-GSC7PVEO.js → chunk-6MV4ESXM.js} +43 -7
- package/dist/chunk-6MV4ESXM.js.map +1 -0
- package/dist/chunk-7BMCLMSE.js +173 -0
- package/dist/chunk-7BMCLMSE.js.map +1 -0
- package/dist/{chunk-NPWYX6NU.js → chunk-BD4ZV7FQ.js} +3 -3
- package/dist/{chunk-BKJ7HYX7.js → chunk-DGLLSZ2D.js} +10 -10
- package/dist/chunk-DGLLSZ2D.js.map +1 -0
- package/dist/chunk-EGJESQXZ.js +1 -0
- package/dist/chunk-I2PC6TBZ.js +3887 -0
- package/dist/chunk-I2PC6TBZ.js.map +1 -0
- package/dist/{chunk-CHWBQJLT.js → chunk-JF7KAVOM.js} +8 -21
- package/dist/chunk-JF7KAVOM.js.map +1 -0
- package/dist/{chunk-DXJJMRBM.js → chunk-LHVSS27T.js} +2 -2
- package/dist/{chunk-2U4TWPWQ.js → chunk-MUINMYCE.js} +5 -5
- package/dist/{chunk-GU5VZGXB.js → chunk-N3NVFSDQ.js} +5 -5
- package/dist/{chunk-CZRJ5GWE.js → chunk-RAP6MUMB.js} +54 -24
- package/dist/chunk-RAP6MUMB.js.map +1 -0
- package/dist/{chunk-ZURDCYGU.js → chunk-RE3PPAXZ.js} +9 -5
- package/dist/chunk-RE3PPAXZ.js.map +1 -0
- package/dist/{chunk-IFL5SREW.js → chunk-UINMHN6S.js} +2 -2
- package/dist/chunk-WBQMGZ5X.js +1253 -0
- package/dist/chunk-WBQMGZ5X.js.map +1 -0
- package/dist/{client-NAL3HVLK.js → client-C3C6IXAH.js} +4 -4
- package/dist/{config-FAQO37S7.js → config-7HU4X7EK.js} +4 -4
- package/dist/{eas-QAH7XVLK.js → eas-G4TPLJ6S.js} +7 -7
- package/dist/{governor-S56M6BV6.js → governor-PYQQ7QRG.js} +7 -7
- package/dist/index.js +209 -1295
- package/dist/index.js.map +1 -1
- package/dist/{network-MKMX2NG6.js → network-WU2QC3L4.js} +3 -3
- package/dist/{price-HHDBMKIE.js → price-DGGB6W2G.js} +15 -37
- package/dist/price-DGGB6W2G.js.map +1 -0
- package/dist/providers-YOOQFBN2.js +31 -0
- package/dist/providers-YOOQFBN2.js.map +1 -0
- package/dist/{research-IJBOMYL3.js → research-KX3LCAOQ.js} +9 -9
- package/dist/{session-L67AVWBY.js → session-GUVSID3Z.js} +19 -174
- package/dist/session-GUVSID3Z.js.map +1 -0
- package/dist/{trade-LGFQEK3I.js → trade-WDPMFFYB.js} +277 -43
- package/dist/trade-WDPMFFYB.js.map +1 -0
- package/dist/{vault-XWFPGHRA.js → vault-WDRLJLJ6.js} +14 -9
- package/dist/{xmtp-6US7GH3Z.js → xmtp-Q4ZSQV2Q.js} +11 -15
- package/dist/xmtp-Q4ZSQV2Q.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunk-BKJ7HYX7.js.map +0 -1
- package/dist/chunk-BQMOE4FJ.js +0 -1629
- package/dist/chunk-BQMOE4FJ.js.map +0 -1
- package/dist/chunk-CHWBQJLT.js.map +0 -1
- package/dist/chunk-CZRJ5GWE.js.map +0 -1
- package/dist/chunk-DIZEB2VV.js +0 -325
- package/dist/chunk-DIZEB2VV.js.map +0 -1
- package/dist/chunk-GSC7PVEO.js.map +0 -1
- package/dist/chunk-ROKE6ZAW.js.map +0 -1
- package/dist/chunk-ZURDCYGU.js.map +0 -1
- package/dist/price-HHDBMKIE.js.map +0 -1
- package/dist/research-4TBGYEVI.js +0 -14
- package/dist/session-L67AVWBY.js.map +0 -1
- package/dist/trade-LGFQEK3I.js.map +0 -1
- package/dist/xmtp-6US7GH3Z.js.map +0 -1
- /package/dist/{abis-KP7M4IPM.js.map → abis-D7EDCKVZ.js.map} +0 -0
- /package/dist/{addresses-DGB3D457.js.map → addresses-OFFTM6NG.js.map} +0 -0
- /package/dist/{agent-UNSW5IPO.js.map → agent-IJOOONON.js.map} +0 -0
- /package/dist/{chunk-NPWYX6NU.js.map → chunk-BD4ZV7FQ.js.map} +0 -0
- /package/dist/{client-NAL3HVLK.js.map → chunk-EGJESQXZ.js.map} +0 -0
- /package/dist/{chunk-DXJJMRBM.js.map → chunk-LHVSS27T.js.map} +0 -0
- /package/dist/{chunk-2U4TWPWQ.js.map → chunk-MUINMYCE.js.map} +0 -0
- /package/dist/{chunk-GU5VZGXB.js.map → chunk-N3NVFSDQ.js.map} +0 -0
- /package/dist/{chunk-IFL5SREW.js.map → chunk-UINMHN6S.js.map} +0 -0
- /package/dist/{config-FAQO37S7.js.map → client-C3C6IXAH.js.map} +0 -0
- /package/dist/{eas-QAH7XVLK.js.map → config-7HU4X7EK.js.map} +0 -0
- /package/dist/{governor-S56M6BV6.js.map → eas-G4TPLJ6S.js.map} +0 -0
- /package/dist/{network-MKMX2NG6.js.map → governor-PYQQ7QRG.js.map} +0 -0
- /package/dist/{research-4TBGYEVI.js.map → network-WU2QC3L4.js.map} +0 -0
- /package/dist/{research-IJBOMYL3.js.map → research-KX3LCAOQ.js.map} +0 -0
- /package/dist/{vault-XWFPGHRA.js.map → vault-WDRLJLJ6.js.map} +0 -0
|
@@ -1,37 +1,258 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getResearchProvider
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import
|
|
5
|
-
UniswapProvider,
|
|
6
|
-
getQuote
|
|
7
|
-
} from "./chunk-DIZEB2VV.js";
|
|
3
|
+
} from "./chunk-DGLLSZ2D.js";
|
|
4
|
+
import "./chunk-EGJESQXZ.js";
|
|
8
5
|
import {
|
|
9
6
|
chatCompletion
|
|
10
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-BD4ZV7FQ.js";
|
|
8
|
+
import {
|
|
9
|
+
getQuote
|
|
10
|
+
} from "./chunk-WBQMGZ5X.js";
|
|
11
11
|
import {
|
|
12
12
|
TOKENS
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UINMHN6S.js";
|
|
14
14
|
import {
|
|
15
15
|
formatContractError,
|
|
16
16
|
getAccount,
|
|
17
|
-
getPublicClient
|
|
18
|
-
|
|
17
|
+
getPublicClient,
|
|
18
|
+
sendTxWithRetry,
|
|
19
|
+
waitForReceipt
|
|
20
|
+
} from "./chunk-2OHX3O55.js";
|
|
19
21
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
getChain,
|
|
23
|
+
getExplorerUrl,
|
|
24
|
+
getNetwork
|
|
25
|
+
} from "./chunk-LHVSS27T.js";
|
|
22
26
|
import {
|
|
27
|
+
getUniswapApiKey,
|
|
23
28
|
loadConfig,
|
|
24
29
|
saveConfig
|
|
25
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-6MV4ESXM.js";
|
|
26
31
|
import {
|
|
27
32
|
ERC20_ABI
|
|
28
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-I2PC6TBZ.js";
|
|
29
34
|
|
|
30
35
|
// src/commands/trade.ts
|
|
31
36
|
import chalk from "chalk";
|
|
32
37
|
import ora from "ora";
|
|
33
38
|
import { confirm } from "@inquirer/prompts";
|
|
34
|
-
import { isAddress, parseUnits as parseUnits2, formatUnits as
|
|
39
|
+
import { isAddress as isAddress2, parseUnits as parseUnits2, formatUnits as formatUnits3 } from "viem";
|
|
40
|
+
|
|
41
|
+
// src/providers/uniswap.ts
|
|
42
|
+
import { isAddress } from "viem";
|
|
43
|
+
var API_BASE = "https://trade-api.gateway.uniswap.org/v1";
|
|
44
|
+
function getApiKey() {
|
|
45
|
+
const key = getUniswapApiKey();
|
|
46
|
+
if (!key) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"Uniswap API key not configured. Run 'sherwood config set --uniswap-api-key <key>' or set UNISWAP_API_KEY env var.\nGet your key at https://developers.uniswap.org/"
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
return key;
|
|
52
|
+
}
|
|
53
|
+
function apiHeaders() {
|
|
54
|
+
return {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
"x-api-key": getApiKey(),
|
|
57
|
+
"x-universal-router-version": "2.0"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function isUniswapXRouting(routing) {
|
|
61
|
+
return routing === "DUTCH_V2" || routing === "DUTCH_V3" || routing === "PRIORITY";
|
|
62
|
+
}
|
|
63
|
+
function getOutputAmount(q) {
|
|
64
|
+
if (isUniswapXRouting(q.routing)) {
|
|
65
|
+
const ux = q;
|
|
66
|
+
const firstOutput = ux.quote.orderInfo.outputs[0];
|
|
67
|
+
if (!firstOutput) throw new Error("UniswapX quote has no outputs");
|
|
68
|
+
return firstOutput.startAmount;
|
|
69
|
+
}
|
|
70
|
+
return q.quote.output.amount;
|
|
71
|
+
}
|
|
72
|
+
async function fetchWithRetry(url, init, maxRetries = 3) {
|
|
73
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
74
|
+
const response = await fetch(url, {
|
|
75
|
+
...init,
|
|
76
|
+
signal: AbortSignal.timeout(3e4)
|
|
77
|
+
});
|
|
78
|
+
if (response.status !== 429 && response.status < 500) return response;
|
|
79
|
+
if (attempt === maxRetries) return response;
|
|
80
|
+
const delay = Math.min(200 * Math.pow(2, attempt) + Math.random() * 100, 1e4);
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
82
|
+
}
|
|
83
|
+
throw new Error("Unreachable");
|
|
84
|
+
}
|
|
85
|
+
var UniswapProvider = class {
|
|
86
|
+
/**
|
|
87
|
+
* Get a quote from the Uniswap Trading API.
|
|
88
|
+
* Returns the optimal route (CLASSIC, PRIORITY, or UniswapX).
|
|
89
|
+
*/
|
|
90
|
+
async quote(params) {
|
|
91
|
+
const account = getAccount();
|
|
92
|
+
const chainId = getChain().id;
|
|
93
|
+
const body = {
|
|
94
|
+
swapper: account.address,
|
|
95
|
+
tokenIn: params.tokenIn,
|
|
96
|
+
tokenOut: params.tokenOut,
|
|
97
|
+
tokenInChainId: String(chainId),
|
|
98
|
+
tokenOutChainId: String(chainId),
|
|
99
|
+
amount: params.amountIn.toString(),
|
|
100
|
+
type: "EXACT_INPUT",
|
|
101
|
+
slippageTolerance: 0.5,
|
|
102
|
+
routingPreference: "BEST_PRICE"
|
|
103
|
+
};
|
|
104
|
+
const res = await fetchWithRetry(`${API_BASE}/quote`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: apiHeaders(),
|
|
107
|
+
body: JSON.stringify(body)
|
|
108
|
+
});
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
const err = await res.text();
|
|
111
|
+
throw new Error(`Uniswap quote failed (${res.status}): ${err}`);
|
|
112
|
+
}
|
|
113
|
+
const data = await res.json();
|
|
114
|
+
const amountOut = BigInt(getOutputAmount(data));
|
|
115
|
+
return {
|
|
116
|
+
amountOut,
|
|
117
|
+
priceImpact: 0,
|
|
118
|
+
route: `${data.routing}: ${params.tokenIn.slice(0, 8)}\u2192${params.tokenOut.slice(0, 8)}`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get a full quote response (preserving the raw API response for swap execution).
|
|
123
|
+
*/
|
|
124
|
+
async fullQuote(params) {
|
|
125
|
+
const account = getAccount();
|
|
126
|
+
const chainId = getChain().id;
|
|
127
|
+
const body = {
|
|
128
|
+
swapper: account.address,
|
|
129
|
+
tokenIn: params.tokenIn,
|
|
130
|
+
tokenOut: params.tokenOut,
|
|
131
|
+
tokenInChainId: String(chainId),
|
|
132
|
+
tokenOutChainId: String(chainId),
|
|
133
|
+
amount: params.amountIn.toString(),
|
|
134
|
+
type: "EXACT_INPUT",
|
|
135
|
+
slippageTolerance: params.slippageTolerance ?? 0.5,
|
|
136
|
+
routingPreference: "BEST_PRICE"
|
|
137
|
+
};
|
|
138
|
+
const res = await fetchWithRetry(`${API_BASE}/quote`, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
headers: apiHeaders(),
|
|
141
|
+
body: JSON.stringify(body)
|
|
142
|
+
});
|
|
143
|
+
if (!res.ok) {
|
|
144
|
+
const err = await res.text();
|
|
145
|
+
throw new Error(`Uniswap quote failed (${res.status}): ${err}`);
|
|
146
|
+
}
|
|
147
|
+
const quoteResponse = await res.json();
|
|
148
|
+
const amountOut = BigInt(getOutputAmount(quoteResponse));
|
|
149
|
+
return { quoteResponse, amountOut, routing: quoteResponse.routing };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check and handle token approval for Uniswap.
|
|
153
|
+
* Uses the Trading API's /check_approval endpoint.
|
|
154
|
+
*/
|
|
155
|
+
async checkApproval(params) {
|
|
156
|
+
const account = getAccount();
|
|
157
|
+
const chainId = getChain().id;
|
|
158
|
+
const client = getPublicClient();
|
|
159
|
+
const res = await fetchWithRetry(`${API_BASE}/check_approval`, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: apiHeaders(),
|
|
162
|
+
body: JSON.stringify({
|
|
163
|
+
walletAddress: account.address,
|
|
164
|
+
token: params.token,
|
|
165
|
+
amount: params.amount.toString(),
|
|
166
|
+
chainId
|
|
167
|
+
})
|
|
168
|
+
});
|
|
169
|
+
if (!res.ok) {
|
|
170
|
+
const err = await res.text();
|
|
171
|
+
throw new Error(`Approval check failed (${res.status}): ${err}`);
|
|
172
|
+
}
|
|
173
|
+
const data = await res.json();
|
|
174
|
+
if (data.approval) {
|
|
175
|
+
const approvalHash = await sendTxWithRetry({
|
|
176
|
+
to: data.approval.to,
|
|
177
|
+
data: data.approval.data,
|
|
178
|
+
value: BigInt(data.approval.value || "0"),
|
|
179
|
+
account,
|
|
180
|
+
chain: getChain()
|
|
181
|
+
});
|
|
182
|
+
await waitForReceipt(approvalHash);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Execute a swap via the Uniswap Trading API.
|
|
187
|
+
*
|
|
188
|
+
* Full flow: check_approval → quote → swap → sign & broadcast.
|
|
189
|
+
*/
|
|
190
|
+
async swap(params) {
|
|
191
|
+
const account = getAccount();
|
|
192
|
+
const client = getPublicClient();
|
|
193
|
+
const ETH_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
194
|
+
if (params.tokenIn !== ETH_ADDRESS2) {
|
|
195
|
+
await this.checkApproval({
|
|
196
|
+
token: params.tokenIn,
|
|
197
|
+
amount: params.amountIn
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const { quoteResponse } = await this.fullQuote({
|
|
201
|
+
tokenIn: params.tokenIn,
|
|
202
|
+
tokenOut: params.tokenOut,
|
|
203
|
+
amountIn: params.amountIn,
|
|
204
|
+
slippageTolerance: params.slippageTolerance ?? 0.5
|
|
205
|
+
});
|
|
206
|
+
const quoteRaw = quoteResponse;
|
|
207
|
+
const { permitData, ...cleanQuote } = quoteRaw;
|
|
208
|
+
const swapRequest = { ...cleanQuote };
|
|
209
|
+
if (permitData && typeof permitData === "object") {
|
|
210
|
+
const typedData = permitData;
|
|
211
|
+
const primaryType = Object.keys(typedData.types).find((k) => k !== "EIP712Domain") ?? "PermitSingle";
|
|
212
|
+
const signature = await account.signTypedData({
|
|
213
|
+
domain: typedData.domain,
|
|
214
|
+
types: typedData.types,
|
|
215
|
+
primaryType,
|
|
216
|
+
message: typedData.values
|
|
217
|
+
});
|
|
218
|
+
swapRequest.signature = signature;
|
|
219
|
+
if (!isUniswapXRouting(quoteResponse.routing)) {
|
|
220
|
+
swapRequest.permitData = permitData;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const swapRes = await fetchWithRetry(`${API_BASE}/swap`, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: apiHeaders(),
|
|
226
|
+
body: JSON.stringify(swapRequest)
|
|
227
|
+
});
|
|
228
|
+
if (!swapRes.ok) {
|
|
229
|
+
const err = await swapRes.text();
|
|
230
|
+
throw new Error(`Uniswap swap failed (${swapRes.status}): ${err}`);
|
|
231
|
+
}
|
|
232
|
+
const swapData = await swapRes.json();
|
|
233
|
+
if (!swapData.swap?.data || swapData.swap.data === "" || swapData.swap.data === "0x") {
|
|
234
|
+
throw new Error("Empty swap data \u2014 quote may have expired. Try again.");
|
|
235
|
+
}
|
|
236
|
+
if (!isAddress(swapData.swap.to) || !isAddress(swapData.swap.from)) {
|
|
237
|
+
throw new Error("Invalid address in swap response");
|
|
238
|
+
}
|
|
239
|
+
const gasLimit = swapData.swap.gasLimit ? BigInt(swapData.swap.gasLimit) * 150n / 100n : void 0;
|
|
240
|
+
const hash = await sendTxWithRetry({
|
|
241
|
+
to: swapData.swap.to,
|
|
242
|
+
data: swapData.swap.data,
|
|
243
|
+
value: BigInt(swapData.swap.value || "0"),
|
|
244
|
+
gas: gasLimit,
|
|
245
|
+
account,
|
|
246
|
+
chain: getChain()
|
|
247
|
+
});
|
|
248
|
+
const receipt = await waitForReceipt(hash);
|
|
249
|
+
return {
|
|
250
|
+
hash,
|
|
251
|
+
success: receipt.status === "success",
|
|
252
|
+
gasUsed: receipt.gasUsed
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
};
|
|
35
256
|
|
|
36
257
|
// src/lib/signals.ts
|
|
37
258
|
var DEFAULT_CONFIG = {
|
|
@@ -278,7 +499,7 @@ function checkExit(entryPrice, currentPrice, highWaterPrice, config, signalResul
|
|
|
278
499
|
}
|
|
279
500
|
|
|
280
501
|
// src/lib/positions.ts
|
|
281
|
-
import { formatUnits, parseUnits } from "viem";
|
|
502
|
+
import { formatUnits as formatUnits2, parseUnits } from "viem";
|
|
282
503
|
function getOpenPositions() {
|
|
283
504
|
const config = loadConfig();
|
|
284
505
|
return config.positions ?? [];
|
|
@@ -328,13 +549,13 @@ async function getCurrentPrice(tokenAddress, tokenDecimals, feeTier) {
|
|
|
328
549
|
amountIn: oneToken,
|
|
329
550
|
fee: feeTier
|
|
330
551
|
});
|
|
331
|
-
return Number(
|
|
552
|
+
return Number(formatUnits2(amountOut, 6));
|
|
332
553
|
}
|
|
333
554
|
|
|
334
555
|
// src/commands/trade.ts
|
|
335
556
|
var uniswap = new UniswapProvider();
|
|
336
557
|
async function loadXmtp() {
|
|
337
|
-
return import("./xmtp-
|
|
558
|
+
return import("./xmtp-Q4ZSQV2Q.js");
|
|
338
559
|
}
|
|
339
560
|
var ETH_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
340
561
|
var KNOWN_MEMECOINS = {
|
|
@@ -359,7 +580,7 @@ async function resolveToken(symbolOrAddress) {
|
|
|
359
580
|
}
|
|
360
581
|
let address;
|
|
361
582
|
let symbol;
|
|
362
|
-
if (
|
|
583
|
+
if (isAddress2(symbolOrAddress)) {
|
|
363
584
|
address = symbolOrAddress;
|
|
364
585
|
const client2 = getPublicClient();
|
|
365
586
|
try {
|
|
@@ -431,8 +652,21 @@ function formatSignalValue(v) {
|
|
|
431
652
|
const s = (v >= 0 ? "+" : "") + v.toFixed(2);
|
|
432
653
|
return v > 0 ? chalk.green(s) : v < 0 ? chalk.red(s) : chalk.dim(s);
|
|
433
654
|
}
|
|
655
|
+
var TRADING_API_NETWORKS = /* @__PURE__ */ new Set(["base", "base-sepolia"]);
|
|
434
656
|
function registerTradeCommands(program) {
|
|
435
657
|
const trade = program.command("trade").description("Memecoin trading \u2014 scan, buy, sell, monitor positions (Uniswap Trading API on Base)");
|
|
658
|
+
trade.hook("preAction", () => {
|
|
659
|
+
const network = getNetwork();
|
|
660
|
+
if (!TRADING_API_NETWORKS.has(network)) {
|
|
661
|
+
console.error(chalk.red(`trade commands are not supported on ${network}.`));
|
|
662
|
+
console.error(
|
|
663
|
+
chalk.dim(
|
|
664
|
+
"They require the Uniswap Trading API, which covers Base only. Sherwood currently deploys on Robinhood testnet (chain 46630)."
|
|
665
|
+
)
|
|
666
|
+
);
|
|
667
|
+
process.exit(1);
|
|
668
|
+
}
|
|
669
|
+
});
|
|
436
670
|
trade.command("scan").description("Analyze token(s) using on-chain, social, and fundamental signals").option("--token <addr|symbol>", "Specific token to analyze (otherwise scans known memecoins)").option("--fund <name>", "Post results to fund chat").option("--yes", "Skip cost confirmation", false).action(async (opts) => {
|
|
437
671
|
const targets = [];
|
|
438
672
|
if (opts.token) {
|
|
@@ -512,22 +746,22 @@ function registerTradeCommands(program) {
|
|
|
512
746
|
const ethBalance = await client.getBalance({ address: account.address });
|
|
513
747
|
if (ethBalance < inputAmount) {
|
|
514
748
|
console.error(chalk.red(
|
|
515
|
-
`Insufficient ETH. Have ${
|
|
749
|
+
`Insufficient ETH. Have ${formatUnits3(ethBalance, 18)}, need ${opts.amount}`
|
|
516
750
|
));
|
|
517
751
|
process.exit(1);
|
|
518
752
|
}
|
|
519
753
|
const wrapSpinner = ora("Wrapping ETH \u2192 WETH...").start();
|
|
520
754
|
try {
|
|
521
|
-
const { writeContractWithRetry, waitForReceipt: waitForReceiptFn } = await import("./client-
|
|
755
|
+
const { writeContractWithRetry: writeContractWithRetry2, waitForReceipt: waitForReceiptFn } = await import("./client-C3C6IXAH.js");
|
|
522
756
|
const wethAddr = TOKENS().WETH;
|
|
523
|
-
const wrapHash = await
|
|
757
|
+
const wrapHash = await writeContractWithRetry2({
|
|
524
758
|
address: wethAddr,
|
|
525
759
|
abi: WETH_ABI,
|
|
526
760
|
functionName: "deposit",
|
|
527
761
|
args: [],
|
|
528
762
|
value: inputAmount,
|
|
529
763
|
account,
|
|
530
|
-
chain: (await import("./network-
|
|
764
|
+
chain: (await import("./network-WU2QC3L4.js")).getChain()
|
|
531
765
|
});
|
|
532
766
|
await waitForReceiptFn(wrapHash);
|
|
533
767
|
wrapSpinner.succeed(`Wrapped ${opts.amount} ETH \u2192 WETH`);
|
|
@@ -545,7 +779,7 @@ function registerTradeCommands(program) {
|
|
|
545
779
|
});
|
|
546
780
|
if (balance < inputAmount) {
|
|
547
781
|
console.error(chalk.red(
|
|
548
|
-
`Insufficient ${inputSymbol}. Have ${
|
|
782
|
+
`Insufficient ${inputSymbol}. Have ${formatUnits3(balance, inputDecimals)}, need ${opts.amount}`
|
|
549
783
|
));
|
|
550
784
|
process.exit(1);
|
|
551
785
|
}
|
|
@@ -562,7 +796,7 @@ function registerTradeCommands(program) {
|
|
|
562
796
|
expectedOut = result.amountOut;
|
|
563
797
|
const routing = result.routing;
|
|
564
798
|
quoteSpinner.succeed(
|
|
565
|
-
`Quote: ${
|
|
799
|
+
`Quote: ${formatUnits3(result.amountOut, decimals)} ${symbol} (${routing} route)`
|
|
566
800
|
);
|
|
567
801
|
} catch (err) {
|
|
568
802
|
quoteSpinner.fail("Quote failed");
|
|
@@ -600,7 +834,7 @@ function registerTradeCommands(program) {
|
|
|
600
834
|
args: [account.address]
|
|
601
835
|
});
|
|
602
836
|
const tokensReceived = tokenBalance > 0n ? tokenBalance : expectedOut;
|
|
603
|
-
const entryPrice = Number(opts.amount) / Number(
|
|
837
|
+
const entryPrice = Number(opts.amount) / Number(formatUnits3(tokensReceived, decimals));
|
|
604
838
|
const exitConfig = {
|
|
605
839
|
stopLossPct: Number(opts.stopLoss) || DEFAULT_EXIT_CONFIG.stopLossPct,
|
|
606
840
|
trailingStopPct: Number(opts.trailingStop) || DEFAULT_EXIT_CONFIG.trailingStopPct,
|
|
@@ -630,7 +864,7 @@ function registerTradeCommands(program) {
|
|
|
630
864
|
console.log(chalk.dim("\u2500".repeat(40)));
|
|
631
865
|
console.log(` Token: ${symbol} (${tokenAddr})`);
|
|
632
866
|
console.log(` Spent: ${opts.amount} ${inputSymbol}`);
|
|
633
|
-
console.log(` Received: ${
|
|
867
|
+
console.log(` Received: ${formatUnits3(tokensReceived, decimals)} ${symbol}`);
|
|
634
868
|
console.log(` Entry: ${entryPrice.toFixed(8)} ${inputSymbol}/${symbol}`);
|
|
635
869
|
console.log(` Stop: -${exitConfig.stopLossPct}%`);
|
|
636
870
|
if (exitConfig.trailingStopPct > 0) {
|
|
@@ -641,15 +875,15 @@ function registerTradeCommands(program) {
|
|
|
641
875
|
}
|
|
642
876
|
console.log(` Tx: ${chalk.dim(getExplorerUrl(txHash))}`);
|
|
643
877
|
try {
|
|
644
|
-
const { createTradeAttestation, getEasScanUrl } = await import("./eas-
|
|
645
|
-
const { getChainContracts } = await import("./config-
|
|
646
|
-
const { getChain: getActiveChain } = await import("./network-
|
|
878
|
+
const { createTradeAttestation, getEasScanUrl } = await import("./eas-G4TPLJ6S.js");
|
|
879
|
+
const { getChainContracts } = await import("./config-7HU4X7EK.js");
|
|
880
|
+
const { getChain: getActiveChain } = await import("./network-WU2QC3L4.js");
|
|
647
881
|
const vaultRecipient = getChainContracts(getActiveChain().id).vault;
|
|
648
882
|
const { uid } = await createTradeAttestation(
|
|
649
883
|
inputAddr,
|
|
650
884
|
tokenAddr,
|
|
651
885
|
inputAmount,
|
|
652
|
-
|
|
886
|
+
formatUnits3(tokensReceived, decimals),
|
|
653
887
|
txHash,
|
|
654
888
|
"BUY",
|
|
655
889
|
vaultRecipient
|
|
@@ -664,7 +898,7 @@ function registerTradeCommands(program) {
|
|
|
664
898
|
await postToChat(
|
|
665
899
|
opts.fund,
|
|
666
900
|
"TRADE_EXECUTED",
|
|
667
|
-
`Bought ${
|
|
901
|
+
`Bought ${formatUnits3(tokensReceived, decimals)} ${symbol} for ${opts.amount} ${inputSymbol} via Uniswap`,
|
|
668
902
|
{ token: symbol, address: tokenAddr, amountIn: opts.amount, inputToken: inputSymbol, txHash }
|
|
669
903
|
);
|
|
670
904
|
}
|
|
@@ -710,7 +944,7 @@ function registerTradeCommands(program) {
|
|
|
710
944
|
expectedOut = result.amountOut;
|
|
711
945
|
const routing = result.routing;
|
|
712
946
|
quoteSpinner.succeed(
|
|
713
|
-
`Quote: ${
|
|
947
|
+
`Quote: ${formatUnits3(result.amountOut, outputDecimals)} ${outputSymbol} (${routing} route)`
|
|
714
948
|
);
|
|
715
949
|
} catch (err) {
|
|
716
950
|
quoteSpinner.fail("Quote failed");
|
|
@@ -739,12 +973,12 @@ function registerTradeCommands(program) {
|
|
|
739
973
|
console.error(chalk.red(formatContractError(err)));
|
|
740
974
|
process.exit(1);
|
|
741
975
|
}
|
|
742
|
-
const outputReceived = Number(
|
|
976
|
+
const outputReceived = Number(formatUnits3(expectedOut, outputDecimals));
|
|
743
977
|
const costBasis = pos ? Number(pos.amountIn) : 0;
|
|
744
978
|
const inputMatchesOutput = pos ? (pos.inputTokenSymbol ?? "USDC").toUpperCase() === outputSymbol.toUpperCase() : outputSymbol.toUpperCase() === "USDC";
|
|
745
979
|
const pnl = costBasis > 0 && inputMatchesOutput ? outputReceived - costBasis : 0;
|
|
746
980
|
const pnlPct = costBasis > 0 && inputMatchesOutput ? pnl / costBasis * 100 : 0;
|
|
747
|
-
const exitPrice = sellAmount > 0n ? outputReceived / Number(
|
|
981
|
+
const exitPrice = sellAmount > 0n ? outputReceived / Number(formatUnits3(sellAmount, decimals)) : 0;
|
|
748
982
|
if (pos) {
|
|
749
983
|
closePosition(tokenAddr, {
|
|
750
984
|
exitPrice,
|
|
@@ -760,7 +994,7 @@ function registerTradeCommands(program) {
|
|
|
760
994
|
console.log(chalk.bold("Position Closed"));
|
|
761
995
|
console.log(chalk.dim("\u2500".repeat(40)));
|
|
762
996
|
console.log(` Token: ${symbol}`);
|
|
763
|
-
console.log(` Sold: ${
|
|
997
|
+
console.log(` Sold: ${formatUnits3(sellAmount, decimals)} ${symbol}`);
|
|
764
998
|
console.log(` Received: ~${outputReceived.toFixed(outputDecimals <= 6 ? 2 : 6)} ${outputSymbol}`);
|
|
765
999
|
if (costBasis > 0 && inputMatchesOutput) {
|
|
766
1000
|
const posInputSymbol = pos?.inputTokenSymbol ?? "USDC";
|
|
@@ -769,15 +1003,15 @@ function registerTradeCommands(program) {
|
|
|
769
1003
|
}
|
|
770
1004
|
console.log(` Tx: ${chalk.dim(getExplorerUrl(txHash))}`);
|
|
771
1005
|
try {
|
|
772
|
-
const { createTradeAttestation, getEasScanUrl } = await import("./eas-
|
|
773
|
-
const { getChainContracts } = await import("./config-
|
|
774
|
-
const { getChain: getActiveChain } = await import("./network-
|
|
1006
|
+
const { createTradeAttestation, getEasScanUrl } = await import("./eas-G4TPLJ6S.js");
|
|
1007
|
+
const { getChainContracts } = await import("./config-7HU4X7EK.js");
|
|
1008
|
+
const { getChain: getActiveChain } = await import("./network-WU2QC3L4.js");
|
|
775
1009
|
const vaultRecipient = getChainContracts(getActiveChain().id).vault;
|
|
776
1010
|
const { uid } = await createTradeAttestation(
|
|
777
1011
|
tokenAddr,
|
|
778
1012
|
outputAddr,
|
|
779
1013
|
sellAmount,
|
|
780
|
-
|
|
1014
|
+
formatUnits3(expectedOut, outputDecimals),
|
|
781
1015
|
txHash,
|
|
782
1016
|
"SELL",
|
|
783
1017
|
vaultRecipient
|
|
@@ -792,7 +1026,7 @@ function registerTradeCommands(program) {
|
|
|
792
1026
|
await postToChat(
|
|
793
1027
|
opts.fund,
|
|
794
1028
|
"TRADE_EXECUTED",
|
|
795
|
-
`Sold ${
|
|
1029
|
+
`Sold ${formatUnits3(sellAmount, decimals)} ${symbol} for ~${outputReceived.toFixed(2)} ${outputSymbol}${inputMatchesOutput && costBasis > 0 ? ` (P&L: ${pnl >= 0 ? "+" : ""}${pnl.toFixed(2)})` : ""}`,
|
|
796
1030
|
{ token: symbol, address: tokenAddr, outputReceived, outputToken: outputSymbol, pnl, pnlPct, txHash }
|
|
797
1031
|
);
|
|
798
1032
|
}
|
|
@@ -812,7 +1046,7 @@ function registerTradeCommands(program) {
|
|
|
812
1046
|
for (const pos of positions) {
|
|
813
1047
|
try {
|
|
814
1048
|
const current = await getCurrentPrice(pos.tokenAddress, pos.tokenDecimals, pos.feeTier);
|
|
815
|
-
const qty = Number(
|
|
1049
|
+
const qty = Number(formatUnits3(BigInt(pos.amountOut), pos.tokenDecimals));
|
|
816
1050
|
const cost = Number(pos.amountIn);
|
|
817
1051
|
const value = qty * current;
|
|
818
1052
|
const pnl = value - cost;
|
|
@@ -900,7 +1134,7 @@ function registerTradeCommands(program) {
|
|
|
900
1134
|
slippageTolerance: 1
|
|
901
1135
|
// 1% for auto-sell
|
|
902
1136
|
});
|
|
903
|
-
const usdcReceived = Number(
|
|
1137
|
+
const usdcReceived = Number(formatUnits3(sellQuote, 6));
|
|
904
1138
|
const costBasis = Number(pos.amountIn);
|
|
905
1139
|
const pnlUsdc = usdcReceived - costBasis;
|
|
906
1140
|
closePosition(pos.tokenAddress, {
|
|
@@ -946,4 +1180,4 @@ function sleep(ms) {
|
|
|
946
1180
|
export {
|
|
947
1181
|
registerTradeCommands
|
|
948
1182
|
};
|
|
949
|
-
//# sourceMappingURL=trade-
|
|
1183
|
+
//# sourceMappingURL=trade-WDPMFFYB.js.map
|