@elisym/sdk 0.5.0 → 0.7.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/dist/index.cjs +282 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +231 -9
- package/dist/index.d.ts +231 -9
- package/dist/index.js +274 -23
- package/dist/index.js.map +1 -1
- package/dist/runtime.cjs +200 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.cts +116 -0
- package/dist/runtime.d.ts +116 -0
- package/dist/runtime.js +193 -0
- package/dist/runtime.js.map +1 -0
- package/dist/skills.cjs +673 -0
- package/dist/skills.cjs.map +1 -0
- package/dist/skills.d.cts +172 -0
- package/dist/skills.d.ts +172 -0
- package/dist/skills.js +660 -0
- package/dist/skills.js.map +1 -0
- package/package.json +21 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
2
|
-
import { pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, address, AccountRole, getProgramDerivedAddress, assertAccountExists, isAddress, getAddressDecoder, fetchEncodedAccount, decodeAccount, getStructDecoder, fixDecoderSize, getBytesDecoder, getU8Decoder, getOptionDecoder, getU16Decoder, getBooleanDecoder, getI64Decoder } from '@solana/kit';
|
|
2
|
+
import { pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, setTransactionMessageComputeUnitLimit, setTransactionMessageComputeUnitPrice, appendTransactionMessageInstructions, signTransactionMessageWithSigners, address, AccountRole, getProgramDerivedAddress, assertAccountExists, isAddress, getAddressDecoder, fetchEncodedAccount, decodeAccount, getStructDecoder, fixDecoderSize, getBytesDecoder, getU8Decoder, getOptionDecoder, getU16Decoder, getBooleanDecoder, getI64Decoder } from '@solana/kit';
|
|
3
3
|
import Decimal2 from 'decimal.js-light';
|
|
4
|
+
import { z } from 'zod';
|
|
4
5
|
import { verifyEvent, finalizeEvent, getPublicKey, nip19, generateSecretKey, SimplePool } from 'nostr-tools';
|
|
5
6
|
import * as nip44 from 'nostr-tools/nip44';
|
|
6
7
|
|
|
@@ -190,7 +191,118 @@ function assertExpiry(createdAt, expirySecs) {
|
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
// src/payment/priorityFee.ts
|
|
195
|
+
var PRIORITY_FEE_FLOOR_MICROLAMPORTS = 1000n;
|
|
196
|
+
var DEFAULT_PERCENTILE = 75;
|
|
197
|
+
var DEFAULT_CACHE_TTL_MS = 1e4;
|
|
198
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
199
|
+
async function estimatePriorityFeeMicroLamports(rpc, options) {
|
|
200
|
+
const percentile = clampPercentile(options?.percentile ?? DEFAULT_PERCENTILE);
|
|
201
|
+
const ttl = options?.ttlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
202
|
+
const accounts = options?.accounts ?? [];
|
|
203
|
+
const key = cacheKey(percentile, accounts);
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
const cached = cache2.get(key);
|
|
206
|
+
if (cached && now < cached.expires) {
|
|
207
|
+
return cached.microLamports;
|
|
208
|
+
}
|
|
209
|
+
const samples = await rpc.getRecentPrioritizationFees(accounts).send();
|
|
210
|
+
const fee = pickPercentileFee(samples, percentile);
|
|
211
|
+
cache2.set(key, { microLamports: fee, expires: now + ttl });
|
|
212
|
+
return fee;
|
|
213
|
+
}
|
|
214
|
+
function clearPriorityFeeCache() {
|
|
215
|
+
cache2.clear();
|
|
216
|
+
}
|
|
217
|
+
function pickPercentileFee(samples, percentile) {
|
|
218
|
+
if (samples.length === 0) {
|
|
219
|
+
return PRIORITY_FEE_FLOOR_MICROLAMPORTS;
|
|
220
|
+
}
|
|
221
|
+
const sorted = samples.map((sample) => BigInt(sample.prioritizationFee)).sort(compareBigInt);
|
|
222
|
+
const clamped = clampPercentile(percentile);
|
|
223
|
+
const indexFloat = clamped / 100 * (sorted.length - 1) | 0;
|
|
224
|
+
const value = sorted[indexFloat];
|
|
225
|
+
return value > PRIORITY_FEE_FLOOR_MICROLAMPORTS ? value : PRIORITY_FEE_FLOOR_MICROLAMPORTS;
|
|
226
|
+
}
|
|
227
|
+
function clampPercentile(value) {
|
|
228
|
+
if (!Number.isFinite(value)) {
|
|
229
|
+
return DEFAULT_PERCENTILE;
|
|
230
|
+
}
|
|
231
|
+
if (value < 0) {
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
if (value > 100) {
|
|
235
|
+
return 100;
|
|
236
|
+
}
|
|
237
|
+
return value;
|
|
238
|
+
}
|
|
239
|
+
function compareBigInt(left, right) {
|
|
240
|
+
if (left < right) {
|
|
241
|
+
return -1;
|
|
242
|
+
}
|
|
243
|
+
if (left > right) {
|
|
244
|
+
return 1;
|
|
245
|
+
}
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
function cacheKey(percentile, accounts) {
|
|
249
|
+
if (accounts.length === 0) {
|
|
250
|
+
return `p:${percentile}`;
|
|
251
|
+
}
|
|
252
|
+
return `p:${percentile}:${[...accounts].sort().join(",")}`;
|
|
253
|
+
}
|
|
254
|
+
var MAX_DESCRIPTION_LENGTH = LIMITS.MAX_DESCRIPTION_LENGTH;
|
|
255
|
+
var MAX_SAFE_LAMPORTS = Number.MAX_SAFE_INTEGER;
|
|
256
|
+
var MAX_EXPIRY_SECS_SCHEMA = 86400;
|
|
257
|
+
var BASE58_RE = /^[1-9A-HJ-NP-Za-km-z]+$/;
|
|
258
|
+
var SOLANA_ADDRESS_LENGTH_RE = /^.{32,44}$/;
|
|
259
|
+
var lamportsSchema = z.number().int().positive().max(MAX_SAFE_LAMPORTS, `amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
260
|
+
var feeAmountSchema = z.number().int().nonnegative().max(MAX_SAFE_LAMPORTS, `fee_amount must be <= ${MAX_SAFE_LAMPORTS}`);
|
|
261
|
+
var solanaAddressSchema = z.string().regex(BASE58_RE, "must be base58").regex(SOLANA_ADDRESS_LENGTH_RE, "must be 32-44 base58 chars");
|
|
262
|
+
var PaymentRequestSchema = z.object({
|
|
263
|
+
recipient: solanaAddressSchema,
|
|
264
|
+
amount: lamportsSchema,
|
|
265
|
+
reference: solanaAddressSchema,
|
|
266
|
+
description: z.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
267
|
+
fee_address: solanaAddressSchema.optional(),
|
|
268
|
+
fee_amount: feeAmountSchema.optional(),
|
|
269
|
+
created_at: z.number().int().positive(),
|
|
270
|
+
expiry_secs: z.number().int().positive().max(MAX_EXPIRY_SECS_SCHEMA, `expiry_secs must be <= ${MAX_EXPIRY_SECS_SCHEMA}`)
|
|
271
|
+
});
|
|
272
|
+
function parsePaymentRequest(input, options) {
|
|
273
|
+
let parsed;
|
|
274
|
+
try {
|
|
275
|
+
parsed = JSON.parse(input);
|
|
276
|
+
} catch (e) {
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
error: { code: "invalid_json", message: `Invalid payment request JSON: ${e}` }
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const result = PaymentRequestSchema.safeParse(parsed);
|
|
283
|
+
if (!result.success) {
|
|
284
|
+
return {
|
|
285
|
+
ok: false,
|
|
286
|
+
error: { code: "schema", message: result.error.message }
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
if (options?.maxAmountLamports !== void 0) {
|
|
290
|
+
if (BigInt(result.data.amount) > options.maxAmountLamports) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
error: {
|
|
294
|
+
code: "amount_exceeds_max",
|
|
295
|
+
message: `Payment amount ${result.data.amount} lamports exceeds approved max ${options.maxAmountLamports}.`
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { ok: true, data: result.data };
|
|
301
|
+
}
|
|
302
|
+
|
|
193
303
|
// src/payment/solana.ts
|
|
304
|
+
var DEFAULT_COMPUTE_UNIT_LIMIT = 2e5;
|
|
305
|
+
var DEFAULT_PRIORITY_FEE_PERCENTILE = 75;
|
|
194
306
|
var REFERENCE_BYTE_LENGTH = 32;
|
|
195
307
|
function isValidSolanaAddress(value) {
|
|
196
308
|
return isAddress(value);
|
|
@@ -247,32 +359,27 @@ var SolanaPaymentStrategy = class {
|
|
|
247
359
|
expiry_secs: expirySecs
|
|
248
360
|
};
|
|
249
361
|
}
|
|
250
|
-
validatePaymentRequest(requestJson, config, expectedRecipient) {
|
|
362
|
+
validatePaymentRequest(requestJson, config, expectedRecipient, options) {
|
|
251
363
|
assertConfig(config);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
code: "invalid_amount",
|
|
261
|
-
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
if (typeof data.recipient !== "string" || !data.recipient) {
|
|
265
|
-
return { code: "missing_recipient", message: "Missing recipient in payment request" };
|
|
364
|
+
const parsed = parsePaymentRequest(requestJson, {
|
|
365
|
+
maxAmountLamports: options?.maxAmountLamports
|
|
366
|
+
});
|
|
367
|
+
if (!parsed.ok) {
|
|
368
|
+
if (parsed.error.code === "invalid_json") {
|
|
369
|
+
return { code: "invalid_json", message: parsed.error.message };
|
|
370
|
+
}
|
|
371
|
+
if (parsed.error.code === "amount_exceeds_max") {
|
|
372
|
+
return { code: "invalid_amount", message: parsed.error.message };
|
|
373
|
+
}
|
|
374
|
+
return { code: "invalid_amount", message: parsed.error.message };
|
|
266
375
|
}
|
|
376
|
+
const data = parsed.data;
|
|
267
377
|
if (!isValidSolanaAddress(data.recipient)) {
|
|
268
378
|
return {
|
|
269
379
|
code: "invalid_recipient_address",
|
|
270
380
|
message: `Invalid Solana address for recipient: ${data.recipient}`
|
|
271
381
|
};
|
|
272
382
|
}
|
|
273
|
-
if (typeof data.reference !== "string" || !data.reference) {
|
|
274
|
-
return { code: "missing_reference", message: "Missing reference in payment request" };
|
|
275
|
-
}
|
|
276
383
|
if (!isValidSolanaAddress(data.reference)) {
|
|
277
384
|
return {
|
|
278
385
|
code: "invalid_reference_address",
|
|
@@ -332,7 +439,7 @@ var SolanaPaymentStrategy = class {
|
|
|
332
439
|
* read-only, non-signer account so providers can detect the payment via
|
|
333
440
|
* `getSignaturesForAddress(reference)`.
|
|
334
441
|
*/
|
|
335
|
-
async buildTransaction(paymentRequest, payerSigner, rpc, config) {
|
|
442
|
+
async buildTransaction(paymentRequest, payerSigner, rpc, config, options) {
|
|
336
443
|
assertConfig(config);
|
|
337
444
|
assertLamports(paymentRequest.amount, "payment amount");
|
|
338
445
|
if (paymentRequest.amount === 0) {
|
|
@@ -351,14 +458,23 @@ var SolanaPaymentStrategy = class {
|
|
|
351
458
|
`Invalid fee address: expected ${treasury}, got ${paymentRequest.fee_address}. Cannot build transaction with redirected fees.`
|
|
352
459
|
);
|
|
353
460
|
}
|
|
354
|
-
const
|
|
461
|
+
const computeUnitLimit = options?.computeUnitLimit ?? DEFAULT_COMPUTE_UNIT_LIMIT;
|
|
462
|
+
if (!Number.isInteger(computeUnitLimit) || computeUnitLimit <= 0) {
|
|
463
|
+
throw new Error(`Invalid computeUnitLimit: ${computeUnitLimit}. Must be a positive integer.`);
|
|
464
|
+
}
|
|
465
|
+
const paymentInstructions = buildPaymentInstructions(paymentRequest, payerSigner);
|
|
466
|
+
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
467
|
+
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE
|
|
468
|
+
});
|
|
355
469
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
356
470
|
const message = pipe(
|
|
357
471
|
createTransactionMessage({ version: 0 }),
|
|
358
472
|
(m) => setTransactionMessageFeePayerSigner(payerSigner, m),
|
|
359
473
|
(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
|
|
474
|
+
(m) => setTransactionMessageComputeUnitLimit(computeUnitLimit, m),
|
|
475
|
+
(m) => setTransactionMessageComputeUnitPrice(priorityFeeMicroLamports, m),
|
|
360
476
|
(m) => appendTransactionMessageInstructions(
|
|
361
|
-
|
|
477
|
+
paymentInstructions,
|
|
362
478
|
m
|
|
363
479
|
)
|
|
364
480
|
);
|
|
@@ -2271,6 +2387,141 @@ function validateAgentName(name) {
|
|
|
2271
2387
|
}
|
|
2272
2388
|
}
|
|
2273
2389
|
|
|
2274
|
-
|
|
2390
|
+
// src/primitives/rateLimiter.ts
|
|
2391
|
+
function createSlidingWindowLimiter(options) {
|
|
2392
|
+
const { windowMs, maxPerWindow, maxKeys } = options;
|
|
2393
|
+
if (windowMs <= 0) {
|
|
2394
|
+
throw new RangeError("windowMs must be > 0");
|
|
2395
|
+
}
|
|
2396
|
+
if (maxPerWindow <= 0) {
|
|
2397
|
+
throw new RangeError("maxPerWindow must be > 0");
|
|
2398
|
+
}
|
|
2399
|
+
if (maxKeys <= 0) {
|
|
2400
|
+
throw new RangeError("maxKeys must be > 0");
|
|
2401
|
+
}
|
|
2402
|
+
const entries = /* @__PURE__ */ new Map();
|
|
2403
|
+
function evictIfNeeded() {
|
|
2404
|
+
while (entries.size > maxKeys) {
|
|
2405
|
+
const oldestKey = entries.keys().next().value;
|
|
2406
|
+
if (oldestKey === void 0) {
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
entries.delete(oldestKey);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
return {
|
|
2413
|
+
peek(key, now = Date.now()) {
|
|
2414
|
+
const entry = entries.get(key);
|
|
2415
|
+
if (!entry) {
|
|
2416
|
+
return { allowed: true, resetAt: now + windowMs, count: 0 };
|
|
2417
|
+
}
|
|
2418
|
+
const cutoff = now - windowMs;
|
|
2419
|
+
const fresh = entry.hits.filter((timestamp) => timestamp > cutoff);
|
|
2420
|
+
return {
|
|
2421
|
+
allowed: fresh.length < maxPerWindow,
|
|
2422
|
+
resetAt: (fresh[0] ?? now) + windowMs,
|
|
2423
|
+
count: fresh.length
|
|
2424
|
+
};
|
|
2425
|
+
},
|
|
2426
|
+
check(key, now = Date.now()) {
|
|
2427
|
+
const entry = entries.get(key) ?? { hits: [] };
|
|
2428
|
+
const cutoff = now - windowMs;
|
|
2429
|
+
const fresh = entry.hits.filter((timestamp) => timestamp > cutoff);
|
|
2430
|
+
if (fresh.length >= maxPerWindow) {
|
|
2431
|
+
entries.delete(key);
|
|
2432
|
+
entries.set(key, { hits: fresh });
|
|
2433
|
+
return {
|
|
2434
|
+
allowed: false,
|
|
2435
|
+
resetAt: (fresh[0] ?? now) + windowMs,
|
|
2436
|
+
count: fresh.length
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
fresh.push(now);
|
|
2440
|
+
entries.delete(key);
|
|
2441
|
+
entries.set(key, { hits: fresh });
|
|
2442
|
+
evictIfNeeded();
|
|
2443
|
+
return {
|
|
2444
|
+
allowed: true,
|
|
2445
|
+
resetAt: (fresh[0] ?? now) + windowMs,
|
|
2446
|
+
count: fresh.length
|
|
2447
|
+
};
|
|
2448
|
+
},
|
|
2449
|
+
prune(now = Date.now()) {
|
|
2450
|
+
const cutoff = now - windowMs;
|
|
2451
|
+
for (const [key, entry] of entries) {
|
|
2452
|
+
const fresh = entry.hits.filter((timestamp) => timestamp > cutoff);
|
|
2453
|
+
if (fresh.length === 0) {
|
|
2454
|
+
entries.delete(key);
|
|
2455
|
+
} else if (fresh.length !== entry.hits.length) {
|
|
2456
|
+
entry.hits = fresh;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
},
|
|
2460
|
+
size() {
|
|
2461
|
+
return entries.size;
|
|
2462
|
+
},
|
|
2463
|
+
reset() {
|
|
2464
|
+
entries.clear();
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
// src/primitives/logRedact.ts
|
|
2470
|
+
var SECRET_REDACT_PATHS = [
|
|
2471
|
+
"*.ELISYM_NOSTR_PRIVATE_KEY",
|
|
2472
|
+
"*.ELISYM_SOLANA_PRIVATE_KEY",
|
|
2473
|
+
"*.nostrPrivateKeyHex",
|
|
2474
|
+
"*.solanaPrivateKeyBase58",
|
|
2475
|
+
"*.secretKey",
|
|
2476
|
+
"*.secret",
|
|
2477
|
+
"ELISYM_NOSTR_PRIVATE_KEY",
|
|
2478
|
+
"ELISYM_SOLANA_PRIVATE_KEY",
|
|
2479
|
+
// Canonical on-disk `.secrets.json` field names. Logging the whole
|
|
2480
|
+
// `secrets` object, or any single field directly, must not leak.
|
|
2481
|
+
"llm_api_key",
|
|
2482
|
+
"nostr_secret_key",
|
|
2483
|
+
"solana_secret_key",
|
|
2484
|
+
"*.llm_api_key",
|
|
2485
|
+
"*.nostr_secret_key",
|
|
2486
|
+
"*.solana_secret_key",
|
|
2487
|
+
"secrets",
|
|
2488
|
+
"*.secrets"
|
|
2489
|
+
];
|
|
2490
|
+
var INPUT_REDACT_PATHS = [
|
|
2491
|
+
"content",
|
|
2492
|
+
"input",
|
|
2493
|
+
"prompt",
|
|
2494
|
+
"*.content",
|
|
2495
|
+
"*.input",
|
|
2496
|
+
"*.prompt",
|
|
2497
|
+
"event.content",
|
|
2498
|
+
"*.event.content",
|
|
2499
|
+
// JobLedger entries carry the raw Nostr event JSON (which embeds
|
|
2500
|
+
// `event.content`) and the full LLM `resultContent` - both are
|
|
2501
|
+
// customer-confidential and must never land in a structured log.
|
|
2502
|
+
"rawEventJson",
|
|
2503
|
+
"resultContent",
|
|
2504
|
+
"*.rawEventJson",
|
|
2505
|
+
"*.resultContent"
|
|
2506
|
+
];
|
|
2507
|
+
var DEFAULT_REDACT_PATHS = [...SECRET_REDACT_PATHS, ...INPUT_REDACT_PATHS];
|
|
2508
|
+
var INPUT_REDACT_LEAVES = /* @__PURE__ */ new Set([
|
|
2509
|
+
"content",
|
|
2510
|
+
"input",
|
|
2511
|
+
"prompt",
|
|
2512
|
+
"rawEventJson",
|
|
2513
|
+
"resultContent"
|
|
2514
|
+
]);
|
|
2515
|
+
function makeCensor() {
|
|
2516
|
+
return (_value, path) => {
|
|
2517
|
+
const last = path[path.length - 1];
|
|
2518
|
+
if (last !== void 0 && INPUT_REDACT_LEAVES.has(last)) {
|
|
2519
|
+
return "[INPUT REDACTED]";
|
|
2520
|
+
}
|
|
2521
|
+
return "[REDACTED]";
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SolanaPaymentStrategy, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
|
|
2275
2526
|
//# sourceMappingURL=index.js.map
|
|
2276
2527
|
//# sourceMappingURL=index.js.map
|