@elisym/sdk 0.10.3 → 0.11.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 +32 -0
- package/dist/agent-store.cjs.map +1 -1
- package/dist/agent-store.js.map +1 -1
- package/dist/index.cjs +137 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -16
- package/dist/index.d.ts +80 -16
- package/dist/index.js +136 -39
- package/dist/index.js.map +1 -1
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var memo = require('@solana-program/memo');
|
|
3
4
|
var system = require('@solana-program/system');
|
|
4
5
|
var token = require('@solana-program/token');
|
|
5
6
|
var kit = require('@solana/kit');
|
|
@@ -64,6 +65,7 @@ var LAMPORTS_PER_SOL = 1e9;
|
|
|
64
65
|
var PROTOCOL_FEE_BPS = 300;
|
|
65
66
|
var PROTOCOL_TREASURY = "GY7vnWMkKpftU4nQ16C2ATkj1JwrQpHhknkaBUn67VTy";
|
|
66
67
|
var PROTOCOL_PROGRAM_ID_DEVNET = "BrX1CRkSgvcjxBvc2bgc3QqgWjinusofDmeP7ZVxvwrE";
|
|
68
|
+
var ELISYM_PROTOCOL_TAG = "ELiZksgwDt41LaeuPDLkUfWgFXhGgVayTMP7L5nTSEL8";
|
|
67
69
|
function getProtocolProgramId(cluster) {
|
|
68
70
|
switch (cluster) {
|
|
69
71
|
case "devnet":
|
|
@@ -119,13 +121,13 @@ function decodeConfig(encodedAccount) {
|
|
|
119
121
|
getConfigDecoder()
|
|
120
122
|
);
|
|
121
123
|
}
|
|
122
|
-
async function fetchConfig(rpc,
|
|
123
|
-
const maybeAccount = await fetchMaybeConfig(rpc,
|
|
124
|
+
async function fetchConfig(rpc, address4, config) {
|
|
125
|
+
const maybeAccount = await fetchMaybeConfig(rpc, address4, config);
|
|
124
126
|
kit.assertAccountExists(maybeAccount);
|
|
125
127
|
return maybeAccount;
|
|
126
128
|
}
|
|
127
|
-
async function fetchMaybeConfig(rpc,
|
|
128
|
-
const maybeAccount = await kit.fetchEncodedAccount(rpc,
|
|
129
|
+
async function fetchMaybeConfig(rpc, address4, config) {
|
|
130
|
+
const maybeAccount = await kit.fetchEncodedAccount(rpc, address4, config);
|
|
129
131
|
return decodeConfig(maybeAccount);
|
|
130
132
|
}
|
|
131
133
|
if (process.env.NODE_ENV !== "production") ;
|
|
@@ -597,7 +599,9 @@ var SolanaPaymentStrategy = class {
|
|
|
597
599
|
if (!Number.isInteger(computeUnitLimit) || computeUnitLimit <= 0) {
|
|
598
600
|
throw new Error(`Invalid computeUnitLimit: ${computeUnitLimit}. Must be a positive integer.`);
|
|
599
601
|
}
|
|
600
|
-
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner
|
|
602
|
+
const paymentInstructions = await buildPaymentInstructions(paymentRequest, payerSigner, {
|
|
603
|
+
jobEventId: options?.jobEventId
|
|
604
|
+
});
|
|
601
605
|
const priorityFeeMicroLamports = options?.priorityFeeMicroLamports ?? await estimatePriorityFeeMicroLamports(rpc, {
|
|
602
606
|
percentile: options?.priorityFeePercentile ?? DEFAULT_PRIORITY_FEE_PERCENTILE
|
|
603
607
|
});
|
|
@@ -894,9 +898,10 @@ function bigIntDelta(post, pre) {
|
|
|
894
898
|
function waitMs(ms) {
|
|
895
899
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
896
900
|
}
|
|
897
|
-
async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
901
|
+
async function buildPaymentInstructions(paymentRequest, payerSigner, options) {
|
|
898
902
|
const recipient = kit.address(paymentRequest.recipient);
|
|
899
903
|
const reference = kit.address(paymentRequest.reference);
|
|
904
|
+
const protocolTag = kit.address(ELISYM_PROTOCOL_TAG);
|
|
900
905
|
const feeAmount = paymentRequest.fee_amount ?? 0;
|
|
901
906
|
const providerAmount = paymentRequest.fee_address && feeAmount > 0 ? paymentRequest.amount - feeAmount : paymentRequest.amount;
|
|
902
907
|
if (providerAmount <= 0) {
|
|
@@ -904,6 +909,7 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
904
909
|
`Fee amount (${feeAmount}) exceeds or equals total amount (${paymentRequest.amount}). Cannot create transaction with non-positive provider amount.`
|
|
905
910
|
);
|
|
906
911
|
}
|
|
912
|
+
const memoInstruction = options?.jobEventId ? memo.getAddMemoInstruction({ memo: `elisym:v1:${options.jobEventId}` }) : null;
|
|
907
913
|
const asset = resolveAssetFromPaymentRequest(paymentRequest);
|
|
908
914
|
if (!asset.mint) {
|
|
909
915
|
const providerTransferIx2 = system.getTransferSolInstruction({
|
|
@@ -911,14 +917,19 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
911
917
|
destination: recipient,
|
|
912
918
|
amount: BigInt(providerAmount)
|
|
913
919
|
});
|
|
914
|
-
const
|
|
920
|
+
const providerTransferIxWithMarkers2 = {
|
|
915
921
|
...providerTransferIx2,
|
|
916
922
|
accounts: [
|
|
917
923
|
...providerTransferIx2.accounts,
|
|
918
|
-
{ address: reference, role: kit.AccountRole.READONLY }
|
|
924
|
+
{ address: reference, role: kit.AccountRole.READONLY },
|
|
925
|
+
{ address: protocolTag, role: kit.AccountRole.READONLY }
|
|
919
926
|
]
|
|
920
927
|
};
|
|
921
|
-
const instructions2 = [
|
|
928
|
+
const instructions2 = [];
|
|
929
|
+
if (memoInstruction) {
|
|
930
|
+
instructions2.push(memoInstruction);
|
|
931
|
+
}
|
|
932
|
+
instructions2.push(providerTransferIxWithMarkers2);
|
|
922
933
|
if (paymentRequest.fee_address && feeAmount > 0) {
|
|
923
934
|
instructions2.push(
|
|
924
935
|
system.getTransferSolInstruction({
|
|
@@ -943,6 +954,9 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
943
954
|
mint
|
|
944
955
|
});
|
|
945
956
|
const instructions = [];
|
|
957
|
+
if (memoInstruction) {
|
|
958
|
+
instructions.push(memoInstruction);
|
|
959
|
+
}
|
|
946
960
|
instructions.push(
|
|
947
961
|
token.getCreateAssociatedTokenIdempotentInstruction(
|
|
948
962
|
{
|
|
@@ -982,11 +996,15 @@ async function buildPaymentInstructions(paymentRequest, payerSigner) {
|
|
|
982
996
|
amount: BigInt(providerAmount),
|
|
983
997
|
decimals: asset.decimals
|
|
984
998
|
});
|
|
985
|
-
const
|
|
999
|
+
const providerTransferIxWithMarkers = {
|
|
986
1000
|
...providerTransferIx,
|
|
987
|
-
accounts: [
|
|
1001
|
+
accounts: [
|
|
1002
|
+
...providerTransferIx.accounts,
|
|
1003
|
+
{ address: reference, role: kit.AccountRole.READONLY },
|
|
1004
|
+
{ address: protocolTag, role: kit.AccountRole.READONLY }
|
|
1005
|
+
]
|
|
988
1006
|
};
|
|
989
|
-
instructions.push(
|
|
1007
|
+
instructions.push(providerTransferIxWithMarkers);
|
|
990
1008
|
if (treasuryAta && paymentRequest.fee_address && feeAmount > 0) {
|
|
991
1009
|
instructions.push(
|
|
992
1010
|
token.getTransferCheckedInstruction({
|
|
@@ -1141,21 +1159,6 @@ var DiscoveryService = class {
|
|
|
1141
1159
|
constructor(pool) {
|
|
1142
1160
|
this.pool = pool;
|
|
1143
1161
|
}
|
|
1144
|
-
/** Count elisym agents (kind:31990 with "elisym" tag). */
|
|
1145
|
-
async fetchAllAgentCount() {
|
|
1146
|
-
const events = await this.pool.querySync({
|
|
1147
|
-
kinds: [KIND_APP_HANDLER],
|
|
1148
|
-
"#t": ["elisym"]
|
|
1149
|
-
});
|
|
1150
|
-
const uniquePubkeys = /* @__PURE__ */ new Set();
|
|
1151
|
-
for (const event of events) {
|
|
1152
|
-
if (!nostrTools.verifyEvent(event)) {
|
|
1153
|
-
continue;
|
|
1154
|
-
}
|
|
1155
|
-
uniquePubkeys.add(event.pubkey);
|
|
1156
|
-
}
|
|
1157
|
-
return uniquePubkeys.size;
|
|
1158
|
-
}
|
|
1159
1162
|
/**
|
|
1160
1163
|
* Fetch a single page of elisym agents with relay-side pagination.
|
|
1161
1164
|
* Uses `until` cursor for Nostr cursor-based pagination.
|
|
@@ -1236,18 +1239,20 @@ var DiscoveryService = class {
|
|
|
1236
1239
|
*
|
|
1237
1240
|
* Ranking algorithm:
|
|
1238
1241
|
* 1. Bucket each agent into 1-minute slots by `lastPaidJobAt` (newest
|
|
1239
|
-
* `payment-completed` feedback timestamp
|
|
1240
|
-
*
|
|
1242
|
+
* `payment-completed` feedback timestamp, gated by a matching kind:6xxx
|
|
1243
|
+
* result from the provider on the same job event). Cold-start agents go
|
|
1244
|
+
* into a sentinel bucket below all populated buckets.
|
|
1241
1245
|
* 2. Within a bucket, sort by positive review rate descending.
|
|
1242
1246
|
* 3. Tiebreak by raw `lastPaidJobAt`, then `lastSeen` (NIP-89 freshness).
|
|
1243
1247
|
*
|
|
1244
|
-
* NOTE: We
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1248
|
+
* NOTE: We do not verify the `tx` signature on-chain - public Solana devnet
|
|
1249
|
+
* RPC rate-limits trivially exceed what discovery needs (N agents * up-to-5
|
|
1250
|
+
* candidates), and the resulting 429s blocked discovery entirely. As a
|
|
1251
|
+
* lighter sybil mitigation we cross-check `payment-completed` feedback
|
|
1252
|
+
* against a kind:6xxx result event authored by the provider on the same
|
|
1253
|
+
* job: a customer can publish a fake `payment-completed`, but they cannot
|
|
1254
|
+
* forge a result event signed by the provider. Tighten with recipient-tied
|
|
1255
|
+
* on-chain checks when the network moves to mainnet with a paid RPC
|
|
1251
1256
|
* provider.
|
|
1252
1257
|
*/
|
|
1253
1258
|
async fetchAgents(network = "devnet", limit) {
|
|
@@ -1290,14 +1295,27 @@ var DiscoveryService = class {
|
|
|
1290
1295
|
),
|
|
1291
1296
|
this.enrichWithMetadata(agents)
|
|
1292
1297
|
]);
|
|
1298
|
+
const deliveredJobsByProvider = /* @__PURE__ */ new Map();
|
|
1293
1299
|
for (const ev of resultEvents) {
|
|
1294
1300
|
if (!nostrTools.verifyEvent(ev)) {
|
|
1295
1301
|
continue;
|
|
1296
1302
|
}
|
|
1297
1303
|
const agent = agentMap.get(ev.pubkey);
|
|
1298
|
-
if (agent
|
|
1304
|
+
if (!agent) {
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1307
|
+
if (ev.created_at > agent.lastSeen) {
|
|
1299
1308
|
agent.lastSeen = ev.created_at;
|
|
1300
1309
|
}
|
|
1310
|
+
const jobEventId = ev.tags.find((t) => t[0] === "e")?.[1];
|
|
1311
|
+
if (jobEventId) {
|
|
1312
|
+
let delivered = deliveredJobsByProvider.get(ev.pubkey);
|
|
1313
|
+
if (!delivered) {
|
|
1314
|
+
delivered = /* @__PURE__ */ new Set();
|
|
1315
|
+
deliveredJobsByProvider.set(ev.pubkey, delivered);
|
|
1316
|
+
}
|
|
1317
|
+
delivered.add(jobEventId);
|
|
1318
|
+
}
|
|
1301
1319
|
}
|
|
1302
1320
|
for (const ev of feedbackEvents) {
|
|
1303
1321
|
if (!nostrTools.verifyEvent(ev)) {
|
|
@@ -1324,7 +1342,9 @@ var DiscoveryService = class {
|
|
|
1324
1342
|
const status = ev.tags.find((t) => t[0] === "status")?.[1];
|
|
1325
1343
|
const txTag = ev.tags.find((t) => t[0] === "tx");
|
|
1326
1344
|
const txSignature = txTag?.[1];
|
|
1327
|
-
|
|
1345
|
+
const jobEventId = ev.tags.find((t) => t[0] === "e")?.[1];
|
|
1346
|
+
const hasDeliveredResult = jobEventId !== void 0 && deliveredJobsByProvider.get(targetPubkey)?.has(jobEventId) === true;
|
|
1347
|
+
if (status === "payment-completed" && typeof txSignature === "string" && txSignature && hasDeliveredResult) {
|
|
1328
1348
|
if (!agent.lastPaidJobAt || ev.created_at > agent.lastPaidJobAt) {
|
|
1329
1349
|
agent.lastPaidJobAt = ev.created_at;
|
|
1330
1350
|
agent.lastPaidJobTx = txSignature;
|
|
@@ -2901,6 +2921,83 @@ async function doVerifyOnce(rpc, txSignature, expectedRecipient) {
|
|
|
2901
2921
|
}
|
|
2902
2922
|
return { verified: false, txSignature: sigStr, reason: "recipient_mismatch" };
|
|
2903
2923
|
}
|
|
2924
|
+
var DEFAULT_LIMIT = 1e3;
|
|
2925
|
+
var NATIVE_KEY = "native";
|
|
2926
|
+
async function aggregateNetworkStats(rpc, options) {
|
|
2927
|
+
const limit = options?.limit ?? DEFAULT_LIMIT;
|
|
2928
|
+
const concurrency = options?.concurrency ?? DEFAULTS.QUERY_MAX_CONCURRENCY;
|
|
2929
|
+
const tag = kit.address(ELISYM_PROTOCOL_TAG);
|
|
2930
|
+
const signatures = await rpc.getSignaturesForAddress(tag, { limit, before: options?.before }).send();
|
|
2931
|
+
const validSigs = signatures.filter((entry) => entry.err === null);
|
|
2932
|
+
if (validSigs.length === 0) {
|
|
2933
|
+
return { jobCount: 0, volumeByAsset: {} };
|
|
2934
|
+
}
|
|
2935
|
+
const volumeByAsset = {};
|
|
2936
|
+
let jobCount = 0;
|
|
2937
|
+
for (let start = 0; start < validSigs.length; start += concurrency) {
|
|
2938
|
+
const batch = validSigs.slice(start, start + concurrency);
|
|
2939
|
+
const txResults = await Promise.all(
|
|
2940
|
+
batch.map(
|
|
2941
|
+
(entry) => rpc.getTransaction(entry.signature, {
|
|
2942
|
+
commitment: "confirmed",
|
|
2943
|
+
encoding: "json",
|
|
2944
|
+
maxSupportedTransactionVersion: 0
|
|
2945
|
+
}).send().catch(() => null)
|
|
2946
|
+
)
|
|
2947
|
+
);
|
|
2948
|
+
for (const tx of txResults) {
|
|
2949
|
+
if (!tx?.meta || tx.meta.err) {
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2952
|
+
jobCount += 1;
|
|
2953
|
+
accumulateTransfers(tx, volumeByAsset);
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
const latest = validSigs[0]?.signature;
|
|
2957
|
+
const oldest = validSigs.at(-1)?.signature;
|
|
2958
|
+
return {
|
|
2959
|
+
jobCount,
|
|
2960
|
+
volumeByAsset,
|
|
2961
|
+
latestSignature: latest,
|
|
2962
|
+
oldestSignature: oldest
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
function accumulateTransfers(tx, volumeByAsset) {
|
|
2966
|
+
const raw = tx;
|
|
2967
|
+
const meta = raw.meta;
|
|
2968
|
+
if (!meta) {
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
const preTokens = meta.preTokenBalances ?? [];
|
|
2972
|
+
const postTokens = meta.postTokenBalances ?? [];
|
|
2973
|
+
const isSpl = postTokens.length > 0 || preTokens.length > 0;
|
|
2974
|
+
if (isSpl) {
|
|
2975
|
+
accumulateSplDeltas(preTokens, postTokens, volumeByAsset);
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
accumulateNativeDeltas(meta.preBalances, meta.postBalances, volumeByAsset);
|
|
2979
|
+
}
|
|
2980
|
+
function accumulateSplDeltas(pre, post, volumeByAsset) {
|
|
2981
|
+
for (const postEntry of post) {
|
|
2982
|
+
const preEntry = pre.find((entry) => entry.accountIndex === postEntry.accountIndex);
|
|
2983
|
+
const preAmount = preEntry ? BigInt(preEntry.uiTokenAmount.amount) : 0n;
|
|
2984
|
+
const postAmount = BigInt(postEntry.uiTokenAmount.amount);
|
|
2985
|
+
const delta = postAmount - preAmount;
|
|
2986
|
+
if (delta > 0n) {
|
|
2987
|
+
volumeByAsset[postEntry.mint] = (volumeByAsset[postEntry.mint] ?? 0n) + delta;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
function accumulateNativeDeltas(pre, post, volumeByAsset) {
|
|
2992
|
+
for (let i = 1; i < post.length; i++) {
|
|
2993
|
+
const preValue = pre[i] ?? 0n;
|
|
2994
|
+
const postValue = post[i] ?? 0n;
|
|
2995
|
+
const delta = BigInt(postValue) - BigInt(preValue);
|
|
2996
|
+
if (delta > 0n) {
|
|
2997
|
+
volumeByAsset[NATIVE_KEY] = (volumeByAsset[NATIVE_KEY] ?? 0n) + delta;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
2904
3001
|
var SessionSpendLimitEntrySchema = zod.z.object({
|
|
2905
3002
|
chain: zod.z.enum(["solana"]),
|
|
2906
3003
|
token: zod.z.string().min(1).max(16).regex(/^[a-z0-9]+$/, "token must be lowercase alphanumeric"),
|
|
@@ -3106,6 +3203,7 @@ exports.DEFAULTS = DEFAULTS;
|
|
|
3106
3203
|
exports.DEFAULT_KIND_OFFSET = DEFAULT_KIND_OFFSET;
|
|
3107
3204
|
exports.DEFAULT_REDACT_PATHS = DEFAULT_REDACT_PATHS;
|
|
3108
3205
|
exports.DiscoveryService = DiscoveryService;
|
|
3206
|
+
exports.ELISYM_PROTOCOL_TAG = ELISYM_PROTOCOL_TAG;
|
|
3109
3207
|
exports.ElisymClient = ElisymClient;
|
|
3110
3208
|
exports.ElisymIdentity = ElisymIdentity;
|
|
3111
3209
|
exports.GlobalConfigSchema = GlobalConfigSchema;
|
|
@@ -3135,6 +3233,7 @@ exports.SECRET_REDACT_PATHS = SECRET_REDACT_PATHS;
|
|
|
3135
3233
|
exports.SessionSpendLimitEntrySchema = SessionSpendLimitEntrySchema;
|
|
3136
3234
|
exports.SolanaPaymentStrategy = SolanaPaymentStrategy;
|
|
3137
3235
|
exports.USDC_SOLANA_DEVNET = USDC_SOLANA_DEVNET;
|
|
3236
|
+
exports.aggregateNetworkStats = aggregateNetworkStats;
|
|
3138
3237
|
exports.assertExpiry = assertExpiry;
|
|
3139
3238
|
exports.assertLamports = assertLamports;
|
|
3140
3239
|
exports.assetByKey = assetByKey;
|