@x1scroll/agent-sdk 1.1.2 → 1.2.1
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/package.json +1 -1
- package/src/index.js +368 -6
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -21,7 +21,8 @@ const {
|
|
|
21
21
|
SystemProgram,
|
|
22
22
|
LAMPORTS_PER_SOL,
|
|
23
23
|
} = require('@solana/web3.js');
|
|
24
|
-
const bs58
|
|
24
|
+
const bs58 = require('bs58');
|
|
25
|
+
const crypto = require('crypto');
|
|
25
26
|
|
|
26
27
|
// ── bs58 compat (v4 vs v5 API shape) ─────────────────────────────────────────
|
|
27
28
|
const bs58encode = (typeof bs58.encode === 'function') ? bs58.encode : bs58.default.encode;
|
|
@@ -51,15 +52,19 @@ const PROGRAM_ID = new PublicKey('ECgaMEwH4KLSz3awDo1vz84mSrx5n6h1ZCrbmunB5UxB')
|
|
|
51
52
|
*/
|
|
52
53
|
const TREASURY = new PublicKey('GmvrL1ymC9ENuQCUqymC9robGa9t9L59AbFiwhDDd4Ld');
|
|
53
54
|
|
|
54
|
-
const DEFAULT_RPC_URL = 'https://rpc
|
|
55
|
+
const DEFAULT_RPC_URL = 'https://x1scroll.io/rpc';
|
|
55
56
|
|
|
56
57
|
// ── Anchor instruction discriminators (sha256("global:<name>")[0..8]) ─────────
|
|
57
58
|
// Pre-computed from the IDL. These are fixed for the deployed program version.
|
|
58
59
|
const DISCRIMINATORS = {
|
|
59
|
-
register_agent:
|
|
60
|
-
store_memory:
|
|
61
|
-
update_agent:
|
|
62
|
-
transfer_agent:
|
|
60
|
+
register_agent: Buffer.from([135, 157, 66, 55, 116, 253, 50, 45]),
|
|
61
|
+
store_memory: Buffer.from([31, 139, 69, 89, 102, 57, 218, 246]),
|
|
62
|
+
update_agent: Buffer.from([220, 76, 168, 212, 224, 211, 185, 76]),
|
|
63
|
+
transfer_agent: Buffer.from([39, 202, 189, 195, 254, 40, 59, 198]),
|
|
64
|
+
// v2 discriminators — sha256("global:<name>")[0:8]
|
|
65
|
+
decision_write: crypto.createHash('sha256').update('global:decision_write').digest().slice(0, 8),
|
|
66
|
+
strategy_branch_open: crypto.createHash('sha256').update('global:strategy_branch_open').digest().slice(0, 8),
|
|
67
|
+
strategy_branch_close: crypto.createHash('sha256').update('global:strategy_branch_close').digest().slice(0, 8),
|
|
63
68
|
};
|
|
64
69
|
|
|
65
70
|
// ── Anchor account discriminators (sha256("account:<Name>")[0..8]) ─────────────
|
|
@@ -134,6 +139,27 @@ function encodeU64(n) {
|
|
|
134
139
|
return buf;
|
|
135
140
|
}
|
|
136
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Pad or truncate a label string to exactly 32 bytes (for branch PDA seeds).
|
|
144
|
+
* @param {string} label
|
|
145
|
+
* @returns {Buffer} 32-byte buffer
|
|
146
|
+
*/
|
|
147
|
+
function padLabel(label) {
|
|
148
|
+
const src = Buffer.from(label, 'utf8');
|
|
149
|
+
const out = Buffer.alloc(32);
|
|
150
|
+
src.copy(out, 0, 0, Math.min(src.length, 32));
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Compute sha256 of a string and return a 32-byte Buffer.
|
|
156
|
+
* @param {string} s
|
|
157
|
+
* @returns {Buffer}
|
|
158
|
+
*/
|
|
159
|
+
function sha256Str(s) {
|
|
160
|
+
return crypto.createHash('sha256').update(s).digest();
|
|
161
|
+
}
|
|
162
|
+
|
|
137
163
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
138
164
|
// Borsh decoding helpers
|
|
139
165
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -329,6 +355,54 @@ class AgentClient {
|
|
|
329
355
|
this._registryCacheExpiry = 0;
|
|
330
356
|
}
|
|
331
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Check RPC connectivity. Throws AgentSDKError with fallback suggestion if unreachable.
|
|
360
|
+
* @returns {Promise<{ ok: boolean, slot: number, rpcUrl: string }>}
|
|
361
|
+
*/
|
|
362
|
+
async healthCheck() {
|
|
363
|
+
const conn = this._getConnection();
|
|
364
|
+
try {
|
|
365
|
+
const slot = await Promise.race([
|
|
366
|
+
conn.getSlot(),
|
|
367
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 8000)),
|
|
368
|
+
]);
|
|
369
|
+
return { ok: true, slot, rpcUrl: this.rpcUrl };
|
|
370
|
+
} catch (err) {
|
|
371
|
+
throw new AgentSDKError(
|
|
372
|
+
`RPC endpoint unreachable: ${this.rpcUrl}. ` +
|
|
373
|
+
`Try the public fallback: https://x1scroll.io/rpc\n` +
|
|
374
|
+
`Original error: ${err.message}`,
|
|
375
|
+
'RPC_UNREACHABLE',
|
|
376
|
+
err
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Check that a keypair has sufficient XNT balance for an operation.
|
|
383
|
+
* Throws AgentSDKError with current balance and required amount if insufficient.
|
|
384
|
+
* @param {Keypair} keypair
|
|
385
|
+
* @param {number} requiredLamports
|
|
386
|
+
* @param {string} [operationName]
|
|
387
|
+
*/
|
|
388
|
+
async _assertSufficientBalance(keypair, requiredLamports, operationName = 'operation') {
|
|
389
|
+
const conn = this._getConnection();
|
|
390
|
+
const balance = await conn.getBalance(keypair.publicKey);
|
|
391
|
+
// Add 5000 lamports buffer for network tx fee
|
|
392
|
+
const totalRequired = requiredLamports + 5000;
|
|
393
|
+
if (balance < totalRequired) {
|
|
394
|
+
const balanceXNT = (balance / 1e9).toFixed(6);
|
|
395
|
+
const requiredXNT = (totalRequired / 1e9).toFixed(6);
|
|
396
|
+
throw new AgentSDKError(
|
|
397
|
+
`Insufficient balance for ${operationName}. ` +
|
|
398
|
+
`Have: ${balanceXNT} XNT, Need: ${requiredXNT} XNT. ` +
|
|
399
|
+
`Fund wallet: ${keypair.publicKey.toBase58()}`,
|
|
400
|
+
'INSUFFICIENT_BALANCE'
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
return balance;
|
|
404
|
+
}
|
|
405
|
+
|
|
332
406
|
// ── Internal ────────────────────────────────────────────────────────────────
|
|
333
407
|
|
|
334
408
|
/** @returns {Connection} */
|
|
@@ -492,6 +566,10 @@ class AgentClient {
|
|
|
492
566
|
|
|
493
567
|
const humanKeypair = this.keypair;
|
|
494
568
|
const agentPubkey = agentKeypair.publicKey;
|
|
569
|
+
|
|
570
|
+
// ── Pre-flight balance check (0.05 XNT registration fee) ──
|
|
571
|
+
await this._assertSufficientBalance(humanKeypair, 50_000_000, 'register_agent (0.05 XNT)');
|
|
572
|
+
|
|
495
573
|
const { pda: agentRecordPDA } = AgentClient.deriveAgentRecord(agentPubkey);
|
|
496
574
|
|
|
497
575
|
// Borsh-encode instruction data: discriminator + name + metadata_uri
|
|
@@ -900,15 +978,299 @@ class AgentClient {
|
|
|
900
978
|
// Most recent first
|
|
901
979
|
return results.reverse();
|
|
902
980
|
}
|
|
981
|
+
|
|
982
|
+
// ── v2 Methods ──────────────────────────────────────────────────────────────
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Write a decision to the on-chain decision tree.
|
|
986
|
+
*
|
|
987
|
+
* @param {Keypair} agentKeypair The agent keypair (signer + fee payer)
|
|
988
|
+
* @param {string} branchLabel Strategy branch this decision belongs to
|
|
989
|
+
* @param {string} cid IPFS CID of the decision payload
|
|
990
|
+
* @param {number} outcome 0=pending, 1=executed, 2=rejected
|
|
991
|
+
* @param {number} confidence 0-10000 basis points (8200 = 82%)
|
|
992
|
+
* @param {Buffer|null} [parentHash] 32-byte parent decision hash; zeros for root
|
|
993
|
+
* @returns {Promise<{ sig: string, decisionHash: string, pda: string }>}
|
|
994
|
+
*/
|
|
995
|
+
async decisionWrite(agentKeypair, branchLabel, cid, outcome, confidence, parentHash = null) {
|
|
996
|
+
assertIsSigner(agentKeypair, 'agentKeypair');
|
|
997
|
+
validateString(branchLabel, 'branchLabel', 64);
|
|
998
|
+
validateString(cid, 'cid', 64);
|
|
999
|
+
|
|
1000
|
+
// ── Pre-flight balance check (0.001 XNT decision write fee) ──
|
|
1001
|
+
await this._assertSufficientBalance(agentKeypair, 1_000_000, 'decision_write (0.001 XNT)');
|
|
1002
|
+
|
|
1003
|
+
if (typeof outcome !== 'number' || ![0, 1, 2].includes(outcome)) {
|
|
1004
|
+
throw new AgentSDKError('outcome must be 0 (pending), 1 (executed), or 2 (rejected)', 'INVALID_INPUT');
|
|
1005
|
+
}
|
|
1006
|
+
if (typeof confidence !== 'number' || confidence < 0 || confidence > 10000) {
|
|
1007
|
+
throw new AgentSDKError('confidence must be 0-10000 basis points', 'INVALID_INPUT');
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Derive decision_hash: sha256(JSON.stringify({cid, branchLabel, timestamp}))
|
|
1011
|
+
const timestamp = Date.now();
|
|
1012
|
+
const decisionHash = sha256Str(JSON.stringify({ cid, branchLabel, timestamp }));
|
|
1013
|
+
|
|
1014
|
+
// parent_hash: provided Buffer(32) or zeros
|
|
1015
|
+
const parentHashBuf = (parentHash instanceof Buffer && parentHash.length === 32)
|
|
1016
|
+
? parentHash
|
|
1017
|
+
: Buffer.alloc(32);
|
|
1018
|
+
|
|
1019
|
+
// Derive AgentRecord PDA
|
|
1020
|
+
const { pda: agentRecordPDA } = AgentClient.deriveAgentRecord(agentKeypair.publicKey);
|
|
1021
|
+
|
|
1022
|
+
// Derive DecisionRecord PDA: [b"decision", agentRecord, decision_hash]
|
|
1023
|
+
const [decisionRecordPDA] = PublicKey.findProgramAddressSync(
|
|
1024
|
+
[Buffer.from('decision'), agentRecordPDA.toBuffer(), decisionHash],
|
|
1025
|
+
PROGRAM_ID
|
|
1026
|
+
);
|
|
1027
|
+
|
|
1028
|
+
// Encode confidence as u32 LE
|
|
1029
|
+
const confidenceBuf = Buffer.alloc(4);
|
|
1030
|
+
confidenceBuf.writeUInt32LE(confidence, 0);
|
|
1031
|
+
|
|
1032
|
+
const data = Buffer.concat([
|
|
1033
|
+
DISCRIMINATORS.decision_write,
|
|
1034
|
+
decisionHash, // [u8;32]
|
|
1035
|
+
parentHashBuf, // [u8;32]
|
|
1036
|
+
encodeString(branchLabel), // string (4-byte LE len + utf8)
|
|
1037
|
+
encodeString(cid), // string
|
|
1038
|
+
Buffer.from([outcome]), // u8
|
|
1039
|
+
confidenceBuf, // u32 LE
|
|
1040
|
+
]);
|
|
1041
|
+
|
|
1042
|
+
const ix = new TransactionInstruction({
|
|
1043
|
+
programId: PROGRAM_ID,
|
|
1044
|
+
keys: [
|
|
1045
|
+
{ pubkey: agentKeypair.publicKey, isSigner: true, isWritable: true }, // agent_authority
|
|
1046
|
+
{ pubkey: agentRecordPDA, isSigner: false, isWritable: true }, // agent_record
|
|
1047
|
+
{ pubkey: decisionRecordPDA, isSigner: false, isWritable: true }, // decision_record (init)
|
|
1048
|
+
{ pubkey: TREASURY, isSigner: false, isWritable: true }, // treasury
|
|
1049
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // system_program
|
|
1050
|
+
],
|
|
1051
|
+
data,
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
const tx = new Transaction().add(ix);
|
|
1055
|
+
const sig = await this._sendAndConfirm(tx, [agentKeypair]);
|
|
1056
|
+
|
|
1057
|
+
return {
|
|
1058
|
+
sig,
|
|
1059
|
+
decisionHash: decisionHash.toString('hex'),
|
|
1060
|
+
pda: decisionRecordPDA.toBase58(),
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/**
|
|
1065
|
+
* Open a new strategy branch on-chain.
|
|
1066
|
+
*
|
|
1067
|
+
* @param {Keypair} agentKeypair The agent keypair (signer + fee payer)
|
|
1068
|
+
* @param {string} label Branch label (max 32 chars — also used for PDA seed)
|
|
1069
|
+
* @param {string} hypothesis Branch hypothesis description (max 256 chars)
|
|
1070
|
+
* @param {string} [parentBranch] Parent branch label, or '' for top-level
|
|
1071
|
+
* @returns {Promise<{ sig: string, branchPda: string }>}
|
|
1072
|
+
*/
|
|
1073
|
+
async branchOpen(agentKeypair, label, hypothesis, parentBranch = '') {
|
|
1074
|
+
assertIsSigner(agentKeypair, 'agentKeypair');
|
|
1075
|
+
validateString(label, 'label', 32);
|
|
1076
|
+
validateString(hypothesis, 'hypothesis', 256);
|
|
1077
|
+
if (parentBranch !== '') {
|
|
1078
|
+
validateString(parentBranch, 'parentBranch', 32);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const { pda: agentRecordPDA } = AgentClient.deriveAgentRecord(agentKeypair.publicKey);
|
|
1082
|
+
|
|
1083
|
+
// Derive BranchRecord PDA: [b"branch", agentRecord, padLabel(label)]
|
|
1084
|
+
const [branchRecordPDA] = PublicKey.findProgramAddressSync(
|
|
1085
|
+
[Buffer.from('branch'), agentRecordPDA.toBuffer(), padLabel(label)],
|
|
1086
|
+
PROGRAM_ID
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
const data = Buffer.concat([
|
|
1090
|
+
DISCRIMINATORS.strategy_branch_open,
|
|
1091
|
+
encodeString(label),
|
|
1092
|
+
encodeString(parentBranch),
|
|
1093
|
+
encodeString(hypothesis),
|
|
1094
|
+
]);
|
|
1095
|
+
|
|
1096
|
+
const ix = new TransactionInstruction({
|
|
1097
|
+
programId: PROGRAM_ID,
|
|
1098
|
+
keys: [
|
|
1099
|
+
{ pubkey: agentKeypair.publicKey, isSigner: true, isWritable: true }, // agent_authority
|
|
1100
|
+
{ pubkey: agentRecordPDA, isSigner: false, isWritable: true }, // agent_record
|
|
1101
|
+
{ pubkey: branchRecordPDA, isSigner: false, isWritable: true }, // branch_record (init)
|
|
1102
|
+
{ pubkey: TREASURY, isSigner: false, isWritable: true }, // treasury
|
|
1103
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // system_program
|
|
1104
|
+
],
|
|
1105
|
+
data,
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
const tx = new Transaction().add(ix);
|
|
1109
|
+
const sig = await this._sendAndConfirm(tx, [agentKeypair]);
|
|
1110
|
+
|
|
1111
|
+
return {
|
|
1112
|
+
sig,
|
|
1113
|
+
branchPda: branchRecordPDA.toBase58(),
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Close a strategy branch on-chain.
|
|
1119
|
+
*
|
|
1120
|
+
* @param {Keypair} agentKeypair The agent keypair (signer + fee payer)
|
|
1121
|
+
* @param {string} label Branch label to close (must match opened label)
|
|
1122
|
+
* @param {number} outcome 1=success, 2=failure, 3=abandoned
|
|
1123
|
+
* @param {string} summaryCid IPFS CID of the closing summary
|
|
1124
|
+
* @returns {Promise<{ sig: string }>}
|
|
1125
|
+
*/
|
|
1126
|
+
async branchClose(agentKeypair, label, outcome, summaryCid) {
|
|
1127
|
+
assertIsSigner(agentKeypair, 'agentKeypair');
|
|
1128
|
+
validateString(label, 'label', 32);
|
|
1129
|
+
validateString(summaryCid, 'summaryCid', 64);
|
|
1130
|
+
|
|
1131
|
+
if (typeof outcome !== 'number' || ![1, 2, 3].includes(outcome)) {
|
|
1132
|
+
throw new AgentSDKError('outcome must be 1 (success), 2 (failure), or 3 (abandoned)', 'INVALID_INPUT');
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
const { pda: agentRecordPDA } = AgentClient.deriveAgentRecord(agentKeypair.publicKey);
|
|
1136
|
+
|
|
1137
|
+
// Derive BranchRecord PDA: [b"branch", agentRecord, padLabel(label)]
|
|
1138
|
+
const [branchRecordPDA] = PublicKey.findProgramAddressSync(
|
|
1139
|
+
[Buffer.from('branch'), agentRecordPDA.toBuffer(), padLabel(label)],
|
|
1140
|
+
PROGRAM_ID
|
|
1141
|
+
);
|
|
1142
|
+
|
|
1143
|
+
const data = Buffer.concat([
|
|
1144
|
+
DISCRIMINATORS.strategy_branch_close,
|
|
1145
|
+
encodeString(label),
|
|
1146
|
+
Buffer.from([outcome]), // u8
|
|
1147
|
+
encodeString(summaryCid),
|
|
1148
|
+
]);
|
|
1149
|
+
|
|
1150
|
+
const ix = new TransactionInstruction({
|
|
1151
|
+
programId: PROGRAM_ID,
|
|
1152
|
+
keys: [
|
|
1153
|
+
{ pubkey: agentKeypair.publicKey, isSigner: true, isWritable: true }, // agent_authority
|
|
1154
|
+
{ pubkey: agentRecordPDA, isSigner: false, isWritable: false }, // agent_record (NOT writable for close)
|
|
1155
|
+
{ pubkey: branchRecordPDA, isSigner: false, isWritable: true }, // branch_record
|
|
1156
|
+
{ pubkey: TREASURY, isSigner: false, isWritable: true }, // treasury
|
|
1157
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // system_program
|
|
1158
|
+
],
|
|
1159
|
+
data,
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
const tx = new Transaction().add(ix);
|
|
1163
|
+
const sig = await this._sendAndConfirm(tx, [agentKeypair]);
|
|
1164
|
+
|
|
1165
|
+
return { sig };
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* Fetch last N decisions/memories from chain for an agent by reading transaction history.
|
|
1170
|
+
* Does NOT call serve_context on-chain (requires validator signature).
|
|
1171
|
+
* Reads transactions directly from RPC and filters for program instructions.
|
|
1172
|
+
*
|
|
1173
|
+
* @param {PublicKey|string} agentPda The AgentRecord PDA address
|
|
1174
|
+
* @param {number} [limit] Max number of entries to return (default 10)
|
|
1175
|
+
* @returns {Promise<{ entries: Array<{slot: number, sig: string, instruction: string, blockTime: number|null}>, count: number }>}
|
|
1176
|
+
*/
|
|
1177
|
+
async contextGet(agentPda, limit = 10) {
|
|
1178
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
1179
|
+
throw new AgentSDKError('limit must be a positive integer', 'INVALID_INPUT');
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const agentPdaPubkey = new PublicKey(agentPda);
|
|
1183
|
+
const connection = this._getConnection();
|
|
1184
|
+
|
|
1185
|
+
// Fetch recent signatures for this address
|
|
1186
|
+
const sigs = await connection.getSignaturesForAddress(agentPdaPubkey, { limit });
|
|
1187
|
+
|
|
1188
|
+
const entries = [];
|
|
1189
|
+
|
|
1190
|
+
// Fetch and parse each transaction
|
|
1191
|
+
await Promise.all(sigs.map(async (sigInfo) => {
|
|
1192
|
+
try {
|
|
1193
|
+
const tx = await connection.getParsedTransaction(sigInfo.signature, {
|
|
1194
|
+
maxSupportedTransactionVersion: 0,
|
|
1195
|
+
});
|
|
1196
|
+
if (!tx || !tx.transaction) return;
|
|
1197
|
+
|
|
1198
|
+
const instructions = tx.transaction.message.instructions || [];
|
|
1199
|
+
for (const ix of instructions) {
|
|
1200
|
+
const programId = ix.programId ? ix.programId.toBase58() : null;
|
|
1201
|
+
if (programId !== PROGRAM_ID.toBase58()) continue;
|
|
1202
|
+
|
|
1203
|
+
// Classify instruction type from data discriminator if available
|
|
1204
|
+
let instructionType = 'Unknown';
|
|
1205
|
+
if (ix.data) {
|
|
1206
|
+
try {
|
|
1207
|
+
const rawData = Buffer.from(bs58decode(ix.data));
|
|
1208
|
+
const disc = rawData.slice(0, 8);
|
|
1209
|
+
if (disc.equals(DISCRIMINATORS.store_memory)) instructionType = 'StoreMemory';
|
|
1210
|
+
else if (disc.equals(DISCRIMINATORS.decision_write)) instructionType = 'DecisionWrite';
|
|
1211
|
+
else if (disc.equals(DISCRIMINATORS.strategy_branch_open)) instructionType = 'BranchOpen';
|
|
1212
|
+
else if (disc.equals(DISCRIMINATORS.strategy_branch_close)) instructionType = 'BranchClose';
|
|
1213
|
+
else if (disc.equals(DISCRIMINATORS.register_agent)) instructionType = 'RegisterAgent';
|
|
1214
|
+
} catch (_) { /* unparseable — leave as Unknown */ }
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
entries.push({
|
|
1218
|
+
slot: sigInfo.slot,
|
|
1219
|
+
sig: sigInfo.signature,
|
|
1220
|
+
instruction: instructionType,
|
|
1221
|
+
blockTime: sigInfo.blockTime || null,
|
|
1222
|
+
});
|
|
1223
|
+
break; // one entry per tx
|
|
1224
|
+
}
|
|
1225
|
+
} catch (_) {
|
|
1226
|
+
// skip transactions we can't parse
|
|
1227
|
+
}
|
|
1228
|
+
}));
|
|
1229
|
+
|
|
1230
|
+
// Sort by slot descending (most recent first)
|
|
1231
|
+
entries.sort((a, b) => b.slot - a.slot);
|
|
1232
|
+
|
|
1233
|
+
return { entries, count: entries.length };
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Utility: derive the AgentRecord PDA for any public key.
|
|
1238
|
+
*
|
|
1239
|
+
* @param {PublicKey|string} agentPublicKey
|
|
1240
|
+
* @returns {{ pda: PublicKey, bump: number }}
|
|
1241
|
+
*/
|
|
1242
|
+
getAgentPda(agentPublicKey) {
|
|
1243
|
+
const [pda, bump] = PublicKey.findProgramAddressSync(
|
|
1244
|
+
[Buffer.from('agent'), new PublicKey(agentPublicKey).toBuffer()],
|
|
1245
|
+
PROGRAM_ID
|
|
1246
|
+
);
|
|
1247
|
+
return { pda, bump };
|
|
1248
|
+
}
|
|
903
1249
|
}
|
|
904
1250
|
|
|
905
1251
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
906
1252
|
// Exports
|
|
907
1253
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
908
1254
|
|
|
1255
|
+
/**
|
|
1256
|
+
* Standalone utility: derive the AgentRecord PDA for any public key.
|
|
1257
|
+
* Mirrors AgentClient#getAgentPda but available without instantiating a client.
|
|
1258
|
+
*
|
|
1259
|
+
* @param {PublicKey|string} agentPublicKey
|
|
1260
|
+
* @returns {{ pda: PublicKey, bump: number }}
|
|
1261
|
+
*/
|
|
1262
|
+
function getAgentPda(agentPublicKey) {
|
|
1263
|
+
const [pda, bump] = PublicKey.findProgramAddressSync(
|
|
1264
|
+
[Buffer.from('agent'), new PublicKey(agentPublicKey).toBuffer()],
|
|
1265
|
+
PROGRAM_ID
|
|
1266
|
+
);
|
|
1267
|
+
return { pda, bump };
|
|
1268
|
+
}
|
|
1269
|
+
|
|
909
1270
|
module.exports = {
|
|
910
1271
|
AgentClient,
|
|
911
1272
|
AgentSDKError,
|
|
1273
|
+
getAgentPda,
|
|
912
1274
|
PROGRAM_ID,
|
|
913
1275
|
TREASURY,
|
|
914
1276
|
DEFAULT_RPC_URL,
|