@candledottv/agent-sdk 0.4.11 → 0.5.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 +144 -8
- package/dist/client.d.ts +241 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/errors.d.ts +98 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +137 -34
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -161,6 +161,51 @@ class JsonRpcError extends Error {
|
|
|
161
161
|
this.data = args.data;
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
+
function operationErrorMessage(cause, ids, flow) {
|
|
165
|
+
const original = cause instanceof Error ? cause.message : String(cause);
|
|
166
|
+
const isTrade = ids.clientTradeId !== undefined;
|
|
167
|
+
const idName = isTrade ? "clientTradeId" : "clientLaunchId";
|
|
168
|
+
const noun = isTrade ? "trade" : "launch";
|
|
169
|
+
const read = isTrade ? "getTradeJob()" : "getLaunchJob()";
|
|
170
|
+
const bundle = ids.bundleId !== undefined ? ` (bundleId ${ids.bundleId}, single-use: for logs only, never resubmitted)` : "";
|
|
171
|
+
const head = `${original} -- ${idName} ${ids.clientTradeId ?? ids.clientLaunchId}${bundle}: ` + "the request may or may not have reached Candle.";
|
|
172
|
+
const tail = `A call with a new id, or with none, is a second, independent ${noun}.`;
|
|
173
|
+
if (flow === "server-executed") {
|
|
174
|
+
return `${head} Read the job for this id (${read}), or retry the call with this same ${idName} and the same request. ${tail}`;
|
|
175
|
+
}
|
|
176
|
+
return `${head} This id identifies the job: read the job for this id (${read}), and check the wallet's recent ` + `transactions before sending again, because a call under the same id can sign and broadcast a second ` + `transaction. ${tail}`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
class CandleOperationError extends Error {
|
|
180
|
+
constructor(args) {
|
|
181
|
+
super(operationErrorMessage(args.cause, args.ids, args.flow), { cause: args.cause });
|
|
182
|
+
this.name = "CandleOperationError";
|
|
183
|
+
Object.assign(this, definedIds(args.ids));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function definedIds(ids) {
|
|
187
|
+
return {
|
|
188
|
+
...typeof ids.clientTradeId === "string" ? { clientTradeId: ids.clientTradeId } : {},
|
|
189
|
+
...typeof ids.clientLaunchId === "string" ? { clientLaunchId: ids.clientLaunchId } : {},
|
|
190
|
+
...typeof ids.bundleId === "string" ? { bundleId: ids.bundleId } : {}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function operationIds(error) {
|
|
194
|
+
if (error instanceof CandleApiError || error instanceof JsonRpcError || error instanceof TradeLandedFeeLegError || error instanceof CandleOperationError) {
|
|
195
|
+
return definedIds(error);
|
|
196
|
+
}
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
199
|
+
function withOperationIds(error, ids, flow) {
|
|
200
|
+
if (error instanceof CandleApiError || error instanceof JsonRpcError)
|
|
201
|
+
return Object.assign(error, definedIds(ids));
|
|
202
|
+
if (error instanceof TradeLandedFeeLegError)
|
|
203
|
+
return error;
|
|
204
|
+
if (error instanceof CandleOperationError) {
|
|
205
|
+
return new CandleOperationError({ cause: error.cause, ids: { ...operationIds(error), ...definedIds(ids) }, flow });
|
|
206
|
+
}
|
|
207
|
+
return new CandleOperationError({ cause: error, ids, flow });
|
|
208
|
+
}
|
|
164
209
|
function envelopeError(body) {
|
|
165
210
|
if (typeof body !== "object" || body === null)
|
|
166
211
|
return null;
|
|
@@ -1105,25 +1150,33 @@ class CandleClient {
|
|
|
1105
1150
|
}
|
|
1106
1151
|
async launch(req) {
|
|
1107
1152
|
this.requireKey("launch()");
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1153
|
+
const clientLaunchId = req.clientLaunchId ?? generateClientLaunchId();
|
|
1154
|
+
const body = { ...req, clientLaunchId };
|
|
1155
|
+
return this.runOperation({ clientLaunchId }, "server-executed", async (op) => {
|
|
1156
|
+
let lastError;
|
|
1157
|
+
for (let attempt = 0;attempt <= this.maxRetries; attempt++) {
|
|
1158
|
+
if (attempt > 0)
|
|
1159
|
+
await sleep2(retryDelayMs(attempt - 1));
|
|
1160
|
+
try {
|
|
1161
|
+
const result = await this.requestJson("POST", "/api/v1/launch/headless", body, op);
|
|
1162
|
+
return { ...result, clientLaunchId };
|
|
1163
|
+
} catch (error) {
|
|
1164
|
+
if (!isRetryableLaunchFailure(error))
|
|
1165
|
+
throw error;
|
|
1166
|
+
lastError = error;
|
|
1167
|
+
}
|
|
1119
1168
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1169
|
+
throw lastError;
|
|
1170
|
+
});
|
|
1122
1171
|
}
|
|
1123
1172
|
async launchAsync(req) {
|
|
1124
1173
|
this.requireKey("launchAsync()");
|
|
1125
|
-
const
|
|
1126
|
-
|
|
1174
|
+
const clientLaunchId = req.clientLaunchId ?? generateClientLaunchId();
|
|
1175
|
+
const body = { ...req, clientLaunchId, async: true };
|
|
1176
|
+
return this.runOperation({ clientLaunchId }, "server-executed", async (op) => {
|
|
1177
|
+
const accepted = await this.requestJson("POST", "/api/v1/launch/headless", body, op);
|
|
1178
|
+
return { ...accepted, clientLaunchId: accepted.clientLaunchId ?? clientLaunchId };
|
|
1179
|
+
});
|
|
1127
1180
|
}
|
|
1128
1181
|
async getLaunchJob(clientLaunchId) {
|
|
1129
1182
|
this.requireKey("getLaunchJob()");
|
|
@@ -1241,16 +1294,28 @@ class CandleClient {
|
|
|
1241
1294
|
return this.requestJson("POST", "/api/v1/launch/self/dry-run", req);
|
|
1242
1295
|
}
|
|
1243
1296
|
async buildSelfLaunch(req) {
|
|
1297
|
+
return this.postSelfLaunchBuild(req);
|
|
1298
|
+
}
|
|
1299
|
+
async postSelfLaunchBuild(req, op) {
|
|
1244
1300
|
this.requireKey("buildSelfLaunch()");
|
|
1245
|
-
return this.requestJson("POST", "/api/v1/launch/self/build", req);
|
|
1301
|
+
return this.requestJson("POST", "/api/v1/launch/self/build", req, op);
|
|
1246
1302
|
}
|
|
1247
1303
|
async confirmSelfLaunch(req) {
|
|
1248
1304
|
this.requireKey("confirmSelfLaunch()");
|
|
1249
|
-
|
|
1305
|
+
const result = await this.requestJson("POST", "/api/v1/launch/self/confirm", req);
|
|
1306
|
+
return { ...result, clientLaunchId: req.clientLaunchId };
|
|
1250
1307
|
}
|
|
1251
1308
|
async buildTrade(req) {
|
|
1309
|
+
return this.postTradeBuild(req);
|
|
1310
|
+
}
|
|
1311
|
+
async postTradeBuild(req, op) {
|
|
1252
1312
|
this.requireKey("buildTrade()");
|
|
1253
|
-
return this.requestJson("POST", "/api/v1/trade/agent/build", req);
|
|
1313
|
+
return this.requestJson("POST", "/api/v1/trade/agent/build", req, op);
|
|
1314
|
+
}
|
|
1315
|
+
async getTradeJob(clientTradeId) {
|
|
1316
|
+
this.requireKey("getTradeJob()");
|
|
1317
|
+
const body = await this.requestJson("GET", `/api/v1/trade/agent/jobs/${encodeURIComponent(clientTradeId)}`);
|
|
1318
|
+
return body.job;
|
|
1254
1319
|
}
|
|
1255
1320
|
async confirmTrade(req) {
|
|
1256
1321
|
this.requireKey("confirmTrade()");
|
|
@@ -1339,15 +1404,22 @@ class CandleClient {
|
|
|
1339
1404
|
...req.maxSlippageBps !== undefined ? { maxSlippageBps: req.maxSlippageBps } : {},
|
|
1340
1405
|
...req.quoteAsset !== undefined ? { quoteAsset: req.quoteAsset } : {}
|
|
1341
1406
|
};
|
|
1407
|
+
const flow = req.from === "main" ? "server-executed" : "caller-broadcast";
|
|
1408
|
+
return this.runOperation({ clientTradeId }, flow, async (op) => {
|
|
1409
|
+
const result = await this.tradeSteps(req, buildReq, op);
|
|
1410
|
+
return typeof result.clientTradeId === "string" ? result : { ...result, clientTradeId };
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
async tradeSteps(req, buildReq, op) {
|
|
1342
1414
|
if (req.from === "main") {
|
|
1343
|
-
const result = await this.
|
|
1415
|
+
const result = await this.postTradeBuild(buildReq, op);
|
|
1344
1416
|
if (result.status !== "executed") {
|
|
1345
1417
|
throw new Error(`trade({ from: "main" }) expected an executed result but got status "${result.status}"`);
|
|
1346
1418
|
}
|
|
1347
1419
|
return result;
|
|
1348
1420
|
}
|
|
1349
1421
|
const { linkedWalletId, privyWalletId } = req.from;
|
|
1350
|
-
const built = await this.
|
|
1422
|
+
const built = await this.postTradeBuild(buildReq, op);
|
|
1351
1423
|
if (built.status !== "built") {
|
|
1352
1424
|
return built;
|
|
1353
1425
|
}
|
|
@@ -1426,11 +1498,12 @@ class CandleClient {
|
|
|
1426
1498
|
}
|
|
1427
1499
|
async selfLaunch(req) {
|
|
1428
1500
|
const { privyWalletId, ...launchReq } = req;
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1501
|
+
const clientLaunchId = launchReq.clientLaunchId ?? generateClientLaunchId();
|
|
1502
|
+
const body = { ...launchReq, clientLaunchId };
|
|
1503
|
+
return this.runOperation({ clientLaunchId }, "caller-broadcast", (op) => this.selfLaunchSteps(body, privyWalletId, op));
|
|
1504
|
+
}
|
|
1505
|
+
async selfLaunchSteps(body, privyWalletId, op) {
|
|
1506
|
+
const built = await this.postSelfLaunchBuild(body, op);
|
|
1434
1507
|
if (typeof built.transaction === "string") {
|
|
1435
1508
|
let unsignedTransactionBase64 = built.transaction;
|
|
1436
1509
|
let clientLaunchId = built.clientLaunchId;
|
|
@@ -1449,7 +1522,7 @@ class CandleClient {
|
|
|
1449
1522
|
throw error;
|
|
1450
1523
|
if (attempt === MAX_BLOCKHASH_REBUILDS)
|
|
1451
1524
|
throw withRpcLagHint(error);
|
|
1452
|
-
const rebuilt = await this.
|
|
1525
|
+
const rebuilt = await this.postSelfLaunchBuild(body, op);
|
|
1453
1526
|
if (typeof rebuilt.transaction !== "string")
|
|
1454
1527
|
throw error;
|
|
1455
1528
|
unsignedTransactionBase64 = rebuilt.transaction;
|
|
@@ -1502,8 +1575,13 @@ class CandleClient {
|
|
|
1502
1575
|
}
|
|
1503
1576
|
async buildAtomicLaunch(req) {
|
|
1504
1577
|
this.requireKey("buildAtomicLaunch()");
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1578
|
+
const clientLaunchId = req.clientLaunchId ?? generateClientLaunchId();
|
|
1579
|
+
const body = { ...req, clientLaunchId };
|
|
1580
|
+
return this.runOperation({ clientLaunchId }, "server-executed", (op) => this.postAtomicBuild(body, clientLaunchId, op));
|
|
1581
|
+
}
|
|
1582
|
+
async postAtomicBuild(body, clientLaunchId, op) {
|
|
1583
|
+
const built = await this.requestJson("POST", "/api/v1/launch/atomic/build", body, op);
|
|
1584
|
+
return { ...built, clientLaunchId };
|
|
1507
1585
|
}
|
|
1508
1586
|
async submitAtomicLaunch(req) {
|
|
1509
1587
|
this.requireKey("submitAtomicLaunch()");
|
|
@@ -1526,14 +1604,24 @@ class CandleClient {
|
|
|
1526
1604
|
throw new Error(`submitAtomicLaunch(): unexpected 200 response shape: ${text}`);
|
|
1527
1605
|
}
|
|
1528
1606
|
async launchAtomic(req) {
|
|
1607
|
+
this.requireKey("buildAtomicLaunch()");
|
|
1529
1608
|
const { payer, firstBuys, ...launchFields } = req;
|
|
1609
|
+
const clientLaunchId = launchFields.clientLaunchId ?? generateClientLaunchId();
|
|
1530
1610
|
const buildReq = {
|
|
1531
1611
|
...launchFields,
|
|
1532
|
-
clientLaunchId
|
|
1612
|
+
clientLaunchId,
|
|
1533
1613
|
payer: toAtomicWirePayer(payer),
|
|
1534
1614
|
firstBuys: firstBuys.map((leg) => ({ payer: toAtomicWirePayer(leg.payer), amountRaw: leg.amountRaw }))
|
|
1535
1615
|
};
|
|
1536
|
-
|
|
1616
|
+
return this.runOperation({ clientLaunchId }, "server-executed", async (op) => {
|
|
1617
|
+
const outcome = await this.launchAtomicSteps(req, buildReq, clientLaunchId, op);
|
|
1618
|
+
return { ...outcome, clientLaunchId };
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
async launchAtomicSteps(req, buildReq, clientLaunchId, op) {
|
|
1622
|
+
const { payer, firstBuys } = req;
|
|
1623
|
+
const built = await this.postAtomicBuild(buildReq, clientLaunchId, op);
|
|
1624
|
+
op.ids.bundleId = built.bundleId;
|
|
1537
1625
|
const signedTxsBase64 = [];
|
|
1538
1626
|
for (const leg of built.legs) {
|
|
1539
1627
|
if (leg.signer !== "client")
|
|
@@ -1869,12 +1957,25 @@ class CandleClient {
|
|
|
1869
1957
|
headers["x-api-key"] = this.apiKey;
|
|
1870
1958
|
return headers;
|
|
1871
1959
|
}
|
|
1872
|
-
async
|
|
1873
|
-
const
|
|
1960
|
+
async runOperation(ids, flow, steps) {
|
|
1961
|
+
const op = { sent: false, ids: { ...ids } };
|
|
1962
|
+
try {
|
|
1963
|
+
return await steps(op);
|
|
1964
|
+
} catch (error) {
|
|
1965
|
+
if (!op.sent)
|
|
1966
|
+
throw error;
|
|
1967
|
+
throw withOperationIds(error, op.ids, flow);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
async requestJson(method, path, body, op) {
|
|
1971
|
+
const init = {
|
|
1874
1972
|
method,
|
|
1875
1973
|
headers: this.headers({ json: body !== undefined }),
|
|
1876
1974
|
...body !== undefined ? { body: JSON.stringify(body) } : {}
|
|
1877
|
-
}
|
|
1975
|
+
};
|
|
1976
|
+
if (op)
|
|
1977
|
+
op.sent = true;
|
|
1978
|
+
const res = await this.fetchImpl(`${this.apiUrl}${path}`, init);
|
|
1878
1979
|
return this.parseResponse(res);
|
|
1879
1980
|
}
|
|
1880
1981
|
async parseResponse(res) {
|
|
@@ -1997,7 +2098,7 @@ function generateClientLaunchId() {
|
|
|
1997
2098
|
function generateClientTradeId() {
|
|
1998
2099
|
return generateSdkId();
|
|
1999
2100
|
}
|
|
2000
|
-
var SDK_VERSION = "0.
|
|
2101
|
+
var SDK_VERSION = "0.5.0";
|
|
2001
2102
|
var sdkUpdateWarned = false;
|
|
2002
2103
|
function noteLatestSdkVersion(value) {
|
|
2003
2104
|
if (sdkUpdateWarned || !value || !/^\d+\.\d+\.\d+$/.test(value))
|
|
@@ -2361,6 +2462,7 @@ export {
|
|
|
2361
2462
|
planPromotionLine,
|
|
2362
2463
|
planLabel,
|
|
2363
2464
|
perpsDepositProblem,
|
|
2465
|
+
operationIds,
|
|
2364
2466
|
isSolanaRpcErrorData,
|
|
2365
2467
|
hyperliquidActionHash,
|
|
2366
2468
|
generateSignerKeypair,
|
|
@@ -2375,6 +2477,7 @@ export {
|
|
|
2375
2477
|
HYPERLIQUID_EXCHANGE_URLS,
|
|
2376
2478
|
HYPERLIQUID_ALLOWED_ACTION_TYPES,
|
|
2377
2479
|
EncryptedFileSecretStore,
|
|
2480
|
+
CandleOperationError,
|
|
2378
2481
|
CandleClient,
|
|
2379
2482
|
CandleApiError,
|
|
2380
2483
|
CANDLE_HYPERLIQUID_BUILDER_ADDRESS,
|
package/package.json
CHANGED