@brainai/satp-client 2.0.1 → 2.0.3

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 CHANGED
@@ -1,18 +1,53 @@
1
- # SATP V3 SDK `@brainai/satp-client`
1
+ # SATP V3 SDK - `@brainai/satp-client`
2
2
 
3
- **Solana Agent Token Protocol** JavaScript/TypeScript SDK for interacting with the SATP V3 devnet programs.
3
+ **Solana Agent Token Protocol** - JavaScript/TypeScript SDK for interacting with the SATP V3 devnet programs.
4
4
 
5
- Version: **3.3.0** | Tests: **101 unit + 16 devnet integration** | Programs: **6**
5
+ Current stable npm package: **@brainai/satp-client@2.0.1** | reviewed rc artifact: **@brainai/satp-client@2.0.2-rc.0** | Programs: **6**
6
6
 
7
7
  ## Installation
8
8
 
9
+ Choose stable, rc, or Git based on what the consumer needs to prove:
10
+
11
+ | Channel | Use when | Command |
12
+ | --- | --- | --- |
13
+ | Stable npm | Default production-style consumption of the stable public package. | `npm install @brainai/satp-client@2.0.1` |
14
+ | Release candidate npm | Validating the reviewed rc package before promotion or producing reproducible rc manifests. | `npm install @brainai/satp-client@2.0.2-rc.0` |
15
+ | Release candidate tag | Quick rc opt-in where a moving dist-tag is acceptable. | `npm install @brainai/satp-client@rc` |
16
+ | Reviewed Git commit | PR coordination or source-review installs tied to an exact SATP commit. | `npm install git+https://github.com/brainAI-bot/satp.git#<SATP_COMMIT>` |
17
+
18
+ The npm `latest` tag still resolves to `@brainai/satp-client@2.0.1`.
19
+ Historical rc-tag readback may still show the older `0.1.0-rc.0` package until
20
+ the rc channel is promoted. The reviewed RC-S6 artifact in this source tree is
21
+ `@brainai/satp-client@2.0.2-rc.0`; downstream apps that need reproducible
22
+ manifests should pin that exact version after promotion instead of relying on
23
+ the moving `@rc` tag.
24
+
25
+ For stable consumer installs, pin the current published npm package:
26
+
27
+ ```bash
28
+ npm install @brainai/satp-client@2.0.1
29
+ ```
30
+
31
+ For exact rc validation:
32
+
33
+ ```bash
34
+ npm install @brainai/satp-client@2.0.2-rc.0
35
+ ```
36
+
37
+ For short-lived rc opt-in:
38
+
39
+ ```bash
40
+ npm install @brainai/satp-client@rc
41
+ ```
42
+
43
+ For branch-only development or PR review, pin an explicit SATP Git commit:
44
+
9
45
  ```bash
10
46
  npm install git+https://github.com/brainAI-bot/satp.git#<SATP_COMMIT>
11
47
  ```
12
48
 
13
- The current review package is `@brainai/satp-client@0.0.0-extraction` and
14
- remains unpublished. Pin a reviewed commit for consumers. Do not publish to npm
15
- until the release packet passes.
49
+ The old `0.0.0-extraction` label was extraction-branch metadata and is not the
50
+ current consumer package. Do not treat branch-only Git installs as npm latest.
16
51
 
17
52
  Mainnet program IDs are intentionally not enabled in this release candidate.
18
53
  Constructors and helpers fail closed for `network: 'mainnet'` until an approved
@@ -72,7 +107,7 @@ const tx = await sdk.buildCreateIdentity(creatorPubkey, 'myAgent', {
72
107
  | `attestations_v3` | `6Xd1dAQJPvQRJ4Ntr6LtPTjDjPUZ8nfnmYLZaZ2DtrdD` | Third-party attestations & proofs |
73
108
  | `reputation_v3` | `2Lz7KzMvKdrGeAuS8WPHu7jK2yScrnKVgacpYVEuDjkJ` | Weighted reputation scoring (CPI → identity) |
74
109
  | `validation_v3` | `6rYRiCYidJYV7QvKrzKGgNu4oMh6BAvynked69R7xMbV` | Validation level computation (CPI → identity) |
75
- | `escrow_v3` | `HXCUWKR2NvRcZ7rNAJHwPcH6QAAWaLR4bRFbfyuDND6C` | SOL escrow for agent jobs |
110
+ | `escrow_v3` | `B1Se8SPx7GLUisa4LYeXY1tDZy5TviJrsV2yMLgqUXmg` | SOL escrow for agent jobs |
76
111
 
77
112
  ## API Reference
78
113
 
@@ -111,6 +146,69 @@ if (!validation.ok) throw new Error(validation.errors.join('; '));
111
146
  re-derives the expected packet so tampered PDA, program, request, or hash fields
112
147
  surface as explicit errors.
113
148
 
149
+ ### x402 Discovery Evidence Lookup Helpers
150
+
151
+ `parseX402DiscoveryMetadata(input)`, `buildX402EvidenceLookup(input, opts)`,
152
+ and `buildRuntimePolicyActionDescriptorFromX402Discovery(input, opts)` map x402
153
+ discovery metadata into SATP runtime policy evidence lookup data. The helpers are
154
+ read-only: x402 payment metadata can identify where evidence may be fetched, but
155
+ it is discovery/evidence lookup only and never authorizes SATP action execution,
156
+ spending, live payment, signing, transactions, or host policy bypass.
157
+
158
+ Run the offline example:
159
+
160
+ ```bash
161
+ node packages/satp-client/examples/x402-discovery-evidence-lookup.js
162
+ ```
163
+
164
+ The example parses discovery metadata, builds an evidence lookup descriptor, and
165
+ builds a runtime policy action descriptor. It asserts
166
+ `X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION` plus
167
+ `paymentAuthorization: false`, `actionAuthorization: false`,
168
+ `spendAuthorized: false`, and `livePaymentRequired: false`.
169
+
170
+ ### Wallet-Control Challenge Helpers
171
+
172
+ `buildWalletControlChallenge(opts)` creates a canonical, offline challenge that
173
+ binds an agent ID to a Solana wallet. It derives the SATP V3 Genesis PDA and
174
+ linked-wallet PDA from `agentId`, `wallet`, and `network`, includes a nonce and
175
+ expiry, and returns plain JSON. It does not connect to RPC, read keypairs,
176
+ create transactions, sign, send, deploy, or mutate chain state.
177
+
178
+ `canonicalWalletControlChallenge(challenge)` returns the exact UTF-8 message a
179
+ wallet signs. `verifyWalletControlChallengeSignature(opts)` verifies a 64-byte
180
+ Ed25519 signature against the challenge wallet and fails closed for mismatched
181
+ wallets, signatures, agent IDs, PDAs, domain, audience, expiry, and replayed
182
+ nonces supplied by your replay cache.
183
+
184
+ ```javascript
185
+ const {
186
+ buildWalletControlChallenge,
187
+ canonicalWalletControlChallenge,
188
+ verifyWalletControlChallengeSignature,
189
+ } = require('@brainai/satp-client');
190
+
191
+ const challenge = buildWalletControlChallenge({
192
+ agentId: 'brainChain',
193
+ wallet: walletPublicKey,
194
+ audience: 'my-service',
195
+ nonce: crypto.randomBytes(16).toString('hex'),
196
+ });
197
+
198
+ // Ask the wallet to sign this exact canonical string.
199
+ const message = canonicalWalletControlChallenge(challenge);
200
+
201
+ const verification = verifyWalletControlChallengeSignature({
202
+ challenge,
203
+ signature,
204
+ expectedWallet: walletPublicKey,
205
+ expectedAgentId: 'brainChain',
206
+ expectedAudience: 'my-service',
207
+ usedNonces: replayCache,
208
+ });
209
+ if (!verification.ok) throw new Error(verification.errors.join('; '));
210
+ ```
211
+
114
212
  ### Constructor
115
213
 
116
214
  ```javascript
@@ -233,7 +331,9 @@ These use Cross-Program Invocation to update fields directly on the Genesis Reco
233
331
 
234
332
  ### Escrow Methods (10)
235
333
 
236
- Full escrow lifecycle for agent marketplace jobs.
334
+ App-agnostic escrow builders for downstream applications that need unsigned
335
+ SATP escrow transactions. Product-specific marketplace records, fees, job
336
+ workflow, moderation, and display copy stay in the consuming application.
237
337
 
238
338
  | Method | Description |
239
339
  |--------|-------------|
@@ -261,13 +361,13 @@ Cancelled Disputed Closed (rent reclaimed)
261
361
  ```
262
362
 
263
363
  ```javascript
264
- // Create an escrow (0.5 SOL for a coding job)
364
+ // Create an escrow (0.5 SOL for a generic work agreement)
265
365
  const tx = await sdk.buildCreateEscrow(
266
366
  clientPubkey,
267
367
  agentWallet,
268
368
  'brainChain',
269
369
  0.5 * 1e9, // lamports
270
- 'Build SATP integration',
370
+ 'Complete agreed work',
271
371
  Math.floor(Date.now()/1000) + 86400 * 7, // 7 day deadline
272
372
  0, // nonce (for multiple escrows with same description)
273
373
  { arbiter: arbiterPubkey }
@@ -328,17 +428,11 @@ const sig = await connection.sendRawTransaction(tx.serialize());
328
428
  await connection.confirmTransaction(sig);
329
429
  ```
330
430
 
331
- ## REST API
332
-
333
- The SATP V3 API is available at `https://agentfolio.bot/api/v3/`:
431
+ ## Consumer APIs
334
432
 
335
- | Endpoint | Description |
336
- |----------|-------------|
337
- | `GET /api/v3/health` | API health + program IDs |
338
- | `GET /api/v3/escrow/by-client/:wallet` | Escrows by client wallet |
339
- | `GET /api/v3/escrow/by-agent/:wallet` | Escrows by agent wallet |
340
- | `GET /api/v3/escrow/by-agent-id/:agentId` | Escrows by SATP agent_id |
341
- | + 18 more | See OpenAPI spec in `docs/` |
433
+ SATP core does not define or host an HTTP API. Downstream applications may wrap
434
+ the SDK with their own read APIs, but those routes are consumer-owned adapters
435
+ and must not be treated as SATP protocol authority.
342
436
 
343
437
  ## Testing
344
438
 
@@ -0,0 +1,495 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SATP V3 SDK - Integration Patterns & App-Agnostic Examples
5
+ *
6
+ * Practical patterns for integrating SATP V3 into applications.
7
+ * Each example is self-contained and can be run against devnet.
8
+ *
9
+ * Usage:
10
+ * node examples/integration-patterns.js [example-name]
11
+ *
12
+ * Examples: lookup, register, review-flow, reputation-flow,
13
+ * attestation-flow, migration, multi-wallet, batch-lookup
14
+ *
15
+ * @author brainChain — brainAI
16
+ * @version 3.0.0
17
+ */
18
+
19
+ const { Connection, PublicKey, Keypair, sendAndConfirmTransaction } = require('@solana/web3.js');
20
+ const {
21
+ SATPV3SDK,
22
+ hashAgentId,
23
+ getGenesisPDA,
24
+ getV3ReviewPDA,
25
+ getV3AttestationPDA,
26
+ getNameRegistryPDA,
27
+ getLinkedWalletPDA,
28
+ } = require('../src');
29
+
30
+ // ═══════════════════════════════════════════════════════
31
+ // PATTERN 1: Agent Identity Lookup (Read-Only)
32
+ // Use case: downstream profile pages and app-owned listings
33
+ // ═══════════════════════════════════════════════════════
34
+
35
+ async function lookupAgent(agentId = 'brainChain') {
36
+ const sdk = new SATPV3SDK({ network: 'devnet' });
37
+
38
+ // Quick existence check (1 RPC call)
39
+ const exists = await sdk.hasIdentity(agentId);
40
+ if (!exists) {
41
+ console.log(`Agent "${agentId}" not found on-chain.`);
42
+ return null;
43
+ }
44
+
45
+ // Full profile read (1 RPC call)
46
+ const record = await sdk.getGenesisRecord(agentId);
47
+
48
+ console.log('=== Agent Profile ===');
49
+ console.log(`Name: ${record.agentName}`);
50
+ console.log(`Category: ${record.category}`);
51
+ console.log(`Description: ${record.description}`);
52
+ console.log(`Capabilities: ${record.capabilities.join(', ')}`);
53
+ console.log(`Active: ${record.isActive}`);
54
+ console.log(`Born: ${record.isBorn}`);
55
+ console.log(`Rep Score: ${record.reputationScore} (${(record.reputationScore / 10000).toFixed(2)}%)`);
56
+ console.log(`Verify Level: L${record.verificationLevel}`);
57
+ console.log(`Authority: ${record.authority}`);
58
+ console.log(`PDA: ${record.pda}`);
59
+ console.log(`Created: ${new Date(record.createdAt * 1000).toISOString()}`);
60
+
61
+ if (record.faceMint) {
62
+ console.log(`Face Mint: ${record.faceMint}`);
63
+ console.log(`Face Image: ${record.faceImage}`);
64
+ }
65
+
66
+ return record;
67
+ }
68
+
69
+ // ═══════════════════════════════════════════════════════
70
+ // PATTERN 2: Agent Registration (Full Flow)
71
+ // Use case: New agent onboarding, self-registration
72
+ // ═══════════════════════════════════════════════════════
73
+
74
+ async function registerAgent(keypairPath) {
75
+ const sdk = new SATPV3SDK({ network: 'devnet' });
76
+
77
+ // In production, load from wallet adapter or server keypair
78
+ // const wallet = Keypair.fromSecretKey(Buffer.from(JSON.parse(fs.readFileSync(keypairPath))));
79
+ // For demo, generate ephemeral
80
+ const wallet = Keypair.generate();
81
+ const agentId = `agent-${Date.now()}`;
82
+
83
+ console.log('=== Step 1: Create Identity ===');
84
+ const { transaction: createTx, genesisPDA } = await sdk.buildCreateIdentity(
85
+ wallet.publicKey,
86
+ agentId,
87
+ {
88
+ name: 'My Agent',
89
+ description: 'An AI agent specialized in data analysis',
90
+ category: 'analytics',
91
+ capabilities: ['data-analysis', 'reporting', 'visualization'],
92
+ metadataUri: 'https://arweave.net/agent-metadata.json',
93
+ }
94
+ );
95
+ console.log(`Genesis PDA: ${genesisPDA.toBase58()}`);
96
+ // await sendAndConfirmTransaction(sdk.connection, createTx, [wallet]);
97
+
98
+ console.log('\n=== Step 2: Register Unique Name ===');
99
+ const { transaction: nameTx, nameRegistryPDA } = await sdk.buildRegisterName(
100
+ wallet.publicKey,
101
+ agentId,
102
+ 'My Agent'
103
+ );
104
+ console.log(`Name Registry PDA: ${nameRegistryPDA.toBase58()}`);
105
+ // await sendAndConfirmTransaction(sdk.connection, nameTx, [wallet]);
106
+
107
+ console.log('\n=== Step 3: Link Wallets ===');
108
+ const hotWallet = Keypair.generate().publicKey;
109
+ const { transaction: linkTx, linkedWalletPDA } = await sdk.buildLinkWallet(
110
+ wallet.publicKey,
111
+ agentId,
112
+ hotWallet,
113
+ 'solana', // chain
114
+ 'hot-wallet' // label
115
+ );
116
+ console.log(`Linked Wallet PDA: ${linkedWalletPDA.toBase58()}`);
117
+ // await sendAndConfirmTransaction(sdk.connection, linkTx, [wallet]);
118
+
119
+ console.log('\n=== Step 4: Init Mint Tracker ===');
120
+ const { transaction: trackerTx, mintTrackerPDA } = await sdk.buildInitMintTracker(
121
+ wallet.publicKey,
122
+ agentId
123
+ );
124
+ console.log(`Mint Tracker PDA: ${mintTrackerPDA.toBase58()}`);
125
+ // await sendAndConfirmTransaction(sdk.connection, trackerTx, [wallet]);
126
+
127
+ console.log('\n✅ Full registration flow built (4 transactions)');
128
+ console.log('Uncomment sendAndConfirmTransaction calls to execute on-chain.');
129
+
130
+ return { agentId, genesisPDA };
131
+ }
132
+
133
+ // ═══════════════════════════════════════════════════════
134
+ // PATTERN 3: Review + Reputation Flow
135
+ // Use case: post-engagement review and consumer-owned feedback
136
+ // ═══════════════════════════════════════════════════════
137
+
138
+ async function reviewAndReputationFlow() {
139
+ const sdk = new SATPV3SDK({ network: 'devnet' });
140
+
141
+ const reviewer = Keypair.generate();
142
+ const agentId = 'brainChain';
143
+ const jobPDA = Keypair.generate().publicKey; // In practice, derive from escrow
144
+
145
+ console.log('=== Review + Reputation Flow ===');
146
+ console.log('This demonstrates the full review → reputation recompute pipeline.\n');
147
+
148
+ // Step 1: Submit a review (requires Reviews program)
149
+ // Note: Review submission uses the Reviews V3 program directly.
150
+ // The SDK provides PDA derivation for reviews:
151
+ const [reviewPDA] = getV3ReviewPDA(jobPDA, reviewer.publicKey, 'devnet');
152
+ console.log(`Review PDA (derived): ${reviewPDA.toBase58()}`);
153
+ console.log('→ Reviews are submitted via the Reviews program (5-star scale).\n');
154
+
155
+ // Step 2: Gather all review accounts for the agent
156
+ // In a real application, you'd query reviews using getProgramAccounts:
157
+ console.log('Querying existing reviews...');
158
+ const connection = new Connection('https://api.devnet.solana.com');
159
+ // Filter reviews by agent — in practice, filter by account data
160
+ // const reviews = await connection.getProgramAccounts(REVIEWS_PROGRAM_ID, { filters: [...] });
161
+
162
+ // Step 3: Trigger reputation recompute (permissionless — anyone can call)
163
+ const caller = Keypair.generate();
164
+ const mockReviewAccounts = [reviewPDA]; // Would be real review PDAs
165
+
166
+ const { transaction: repTx } = await sdk.buildRecomputeReputation(
167
+ caller.publicKey,
168
+ agentId,
169
+ mockReviewAccounts
170
+ );
171
+ console.log('Reputation recompute transaction built.');
172
+ console.log(`Includes ${mockReviewAccounts.length} review account(s) as remaining_accounts.`);
173
+ console.log('→ Score = time-decay weighted average × 200,000 (5★ = 1,000,000)');
174
+ console.log('→ Base score (no reviews) = 500,000\n');
175
+
176
+ // Step 4: Read updated reputation
177
+ // After submitting the recompute tx, read the updated Genesis Record:
178
+ // const updated = await sdk.getGenesisRecord(agentId);
179
+ // console.log(`New reputation score: ${updated.reputationScore}`);
180
+
181
+ console.log('✅ Review → Reputation pipeline demonstrated');
182
+ }
183
+
184
+ // ═══════════════════════════════════════════════════════
185
+ // PATTERN 4: Attestation + Validation Flow
186
+ // Use case: Identity verification, skill certification
187
+ // ═══════════════════════════════════════════════════════
188
+
189
+ async function attestationAndValidationFlow() {
190
+ const sdk = new SATPV3SDK({ network: 'devnet' });
191
+
192
+ const issuer = Keypair.generate(); // Verification authority
193
+ const agentId = 'brainChain';
194
+
195
+ console.log('=== Attestation + Validation Flow ===');
196
+ console.log('Demonstrates: create → verify → recompute level.\n');
197
+
198
+ // Step 1: Create attestation
199
+ const attestationType = 'kyc-identity';
200
+ const proofData = JSON.stringify({
201
+ method: 'document-verification',
202
+ provider: 'brainAI-verify',
203
+ timestamp: Date.now(),
204
+ confidence: 0.98,
205
+ });
206
+
207
+ const { transaction: attTx, attestationPDA } = await sdk.buildCreateAttestation(
208
+ issuer.publicKey,
209
+ agentId,
210
+ attestationType,
211
+ proofData,
212
+ null // No expiry (permanent)
213
+ );
214
+ console.log(`Attestation PDA: ${attestationPDA.toBase58()}`);
215
+ console.log(`Type: ${attestationType}`);
216
+ // await sendAndConfirmTransaction(sdk.connection, attTx, [issuer]);
217
+
218
+ // Step 2: Verify the attestation (issuer confirms)
219
+ const { transaction: verifyTx } = await sdk.buildVerifyAttestation(
220
+ issuer.publicKey,
221
+ attestationPDA
222
+ );
223
+ console.log('Verification transaction built.');
224
+ // await sendAndConfirmTransaction(sdk.connection, verifyTx, [issuer]);
225
+
226
+ // Step 3: Recompute validation level (permissionless)
227
+ const caller = Keypair.generate();
228
+ const { transaction: valTx } = await sdk.buildRecomputeLevel(
229
+ caller.publicKey,
230
+ agentId,
231
+ [attestationPDA]
232
+ );
233
+ console.log('Validation level recompute transaction built.\n');
234
+ // await sendAndConfirmTransaction(sdk.connection, valTx, [caller]);
235
+
236
+ // Validation Level Map:
237
+ console.log('Verification Levels:');
238
+ console.log(' L0 = Unverified (0 unique types)');
239
+ console.log(' L1 = Basic (1 unique type)');
240
+ console.log(' L2 = Verified (2 unique types)');
241
+ console.log(' L3 = Trusted (3 unique types)');
242
+ console.log(' L4 = Certified (4 unique types)');
243
+ console.log(' L5 = Sovereign (5+ unique types)\n');
244
+
245
+ // Attestation types that count toward level:
246
+ console.log('Example attestation types:');
247
+ console.log(' - kyc-identity: Government ID verification');
248
+ console.log(' - code-audit: Code security audit passed');
249
+ console.log(' - performance: Performance benchmark attestation');
250
+ console.log(' - domain-expert: Domain expertise certification');
251
+ console.log(' - community: Community vouching/endorsement');
252
+
253
+ console.log('\n✅ Attestation → Validation pipeline demonstrated');
254
+ }
255
+
256
+ // ═══════════════════════════════════════════════════════
257
+ // PATTERN 5: V2 → V3 Migration
258
+ // Use case: Existing agents upgrading to Genesis Records
259
+ // ═══════════════════════════════════════════════════════
260
+
261
+ async function migrationFlow() {
262
+ const sdk = new SATPV3SDK({ network: 'devnet' });
263
+ const v2Authority = Keypair.generate();
264
+ const agentId = 'legacy-agent';
265
+
266
+ console.log('=== V2 → V3 Migration ===');
267
+ console.log('Migrates existing V2 identity to V3 Genesis Record.\n');
268
+
269
+ // The migration instruction:
270
+ // 1. Verifies the caller is the V2 authority
271
+ // 2. Creates a new V3 Genesis Record with provided metadata
272
+ // 3. Sets the V2 authority as the V3 authority
273
+ // 4. Does NOT modify the V2 account (non-destructive)
274
+
275
+ const { transaction: migrateTx, genesisPDA } = await sdk.buildMigrateV2ToV3(
276
+ v2Authority.publicKey,
277
+ agentId,
278
+ {
279
+ name: 'Legacy Agent (V3)',
280
+ description: 'Migrated from V2 identity',
281
+ category: 'general',
282
+ capabilities: ['chat', 'search'],
283
+ metadataUri: '',
284
+ }
285
+ );
286
+
287
+ console.log(`New V3 Genesis PDA: ${genesisPDA.toBase58()}`);
288
+ console.log('Migration preserves:');
289
+ console.log(' ✓ Authority (same signer)');
290
+ console.log(' ✓ Agent ID hash (deterministic)');
291
+ console.log(' ✓ V2 account (untouched)');
292
+ console.log('Migration adds:');
293
+ console.log(' + Genesis Record with face fields');
294
+ console.log(' + Name Registry support');
295
+ console.log(' + Multi-wallet linking');
296
+ console.log(' + CPI-based reputation/validation');
297
+ console.log(' + Mint tracking (cap: 3)');
298
+
299
+ console.log('\n✅ Migration flow demonstrated');
300
+ }
301
+
302
+ // ═══════════════════════════════════════════════════════
303
+ // PATTERN 6: Batch Agent Lookup (Read-Only)
304
+ // Use case: Marketplace search results, leaderboard
305
+ // ═══════════════════════════════════════════════════════
306
+
307
+ async function batchLookup(agentIds = ['brainChain', 'brainForge', 'brainGrowth']) {
308
+ const sdk = new SATPV3SDK({ network: 'devnet' });
309
+
310
+ console.log('=== Batch Agent Lookup ===');
311
+ console.log(`Looking up ${agentIds.length} agents...\n`);
312
+
313
+ // Method 1: Sequential (simple, rate-limit safe)
314
+ const results = [];
315
+ for (const id of agentIds) {
316
+ const record = await sdk.getGenesisRecord(id);
317
+ results.push({ agentId: id, record });
318
+ }
319
+
320
+ // Method 2: Parallel with getMultipleAccountsInfo (more efficient)
321
+ // Derive all PDAs first (no RPC needed), then batch fetch:
322
+ const pdas = agentIds.map(id => getGenesisPDA(id, 'devnet')[0]);
323
+ const accounts = await sdk.connection.getMultipleAccountsInfo(pdas);
324
+
325
+ console.log('Results:');
326
+ for (let i = 0; i < agentIds.length; i++) {
327
+ const record = results[i].record;
328
+ if (record && !record.error) {
329
+ console.log(` ${record.agentName}: Rep=${record.reputationScore}, L${record.verificationLevel}, Active=${record.isActive}`);
330
+ } else {
331
+ console.log(` ${agentIds[i]}: not found`);
332
+ }
333
+ }
334
+
335
+ // Sorting example (for leaderboard):
336
+ const sorted = results
337
+ .filter(r => r.record && !r.record.error)
338
+ .sort((a, b) => b.record.reputationScore - a.record.reputationScore);
339
+
340
+ console.log('\nLeaderboard (by reputation):');
341
+ sorted.forEach((r, i) => {
342
+ console.log(` ${i + 1}. ${r.record.agentName} — ${r.record.reputationScore}`);
343
+ });
344
+
345
+ console.log('\n✅ Batch lookup demonstrated');
346
+ }
347
+
348
+ // ═══════════════════════════════════════════════════════
349
+ // PATTERN 7: Authority Rotation (2-Step)
350
+ // Use case: Key rotation, team handoff, security recovery
351
+ // ═══════════════════════════════════════════════════════
352
+
353
+ async function authorityRotation() {
354
+ const sdk = new SATPV3SDK({ network: 'devnet' });
355
+
356
+ const currentAuth = Keypair.generate();
357
+ const newAuth = Keypair.generate();
358
+ const agentId = 'my-agent';
359
+
360
+ console.log('=== 2-Step Authority Rotation ===');
361
+ console.log('Secure key rotation with propose → accept pattern.\n');
362
+
363
+ // Step 1: Current authority proposes new authority
364
+ const { transaction: proposeTx } = await sdk.buildProposeAuthority(
365
+ currentAuth.publicKey,
366
+ agentId,
367
+ newAuth.publicKey
368
+ );
369
+ console.log(`Step 1: Propose ${newAuth.publicKey.toBase58().slice(0, 8)}... as new authority`);
370
+ // await sendAndConfirmTransaction(sdk.connection, proposeTx, [currentAuth]);
371
+
372
+ // Step 2: New authority accepts
373
+ const { transaction: acceptTx } = await sdk.buildAcceptAuthority(
374
+ newAuth.publicKey,
375
+ agentId
376
+ );
377
+ console.log(`Step 2: New authority accepts control`);
378
+ // await sendAndConfirmTransaction(sdk.connection, acceptTx, [newAuth]);
379
+
380
+ // Cancel option (current auth can cancel before acceptance):
381
+ const { transaction: cancelTx } = await sdk.buildCancelAuthorityTransfer(
382
+ currentAuth.publicKey,
383
+ agentId
384
+ );
385
+ console.log(`(Optional: Cancel transfer before acceptance)\n`);
386
+
387
+ console.log('Security properties:');
388
+ console.log(' ✓ Current authority must initiate (prevents unauthorized rotation)');
389
+ console.log(' ✓ New authority must confirm (prevents accidental rotation)');
390
+ console.log(' ✓ Current auth can cancel anytime before acceptance');
391
+ console.log(' ✓ After acceptance, old authority has NO access');
392
+
393
+ console.log('\n✅ Authority rotation demonstrated');
394
+ }
395
+
396
+ // ═══════════════════════════════════════════════════════
397
+ // PATTERN 8: PDA Derivation (Offline / No RPC)
398
+ // Use case: Pre-computing addresses, caching, indexers
399
+ // ═══════════════════════════════════════════════════════
400
+
401
+ function offlinePDADerivation() {
402
+ console.log('=== Offline PDA Derivation ===');
403
+ console.log('All PDAs can be derived without any RPC calls.\n');
404
+
405
+ const agentId = 'brainChain';
406
+ const sdk = new SATPV3SDK({ network: 'devnet' });
407
+
408
+ // All at once
409
+ const pdas = sdk.getV3PDAs(agentId);
410
+ console.log('Agent PDAs:');
411
+ console.log(` Genesis: ${pdas.genesis}`);
412
+ console.log(` Mint Tracker: ${pdas.mintTracker}`);
413
+ console.log(` Rep Authority: ${pdas.reputationAuthority}`);
414
+ console.log(` Val Authority: ${pdas.validationAuthority}`);
415
+ console.log(` Agent ID Hash: ${pdas.agentIdHash.slice(0, 16)}...`);
416
+
417
+ // Individual derivations
418
+ const wallet = Keypair.generate().publicKey;
419
+ const [genesisPDA] = getGenesisPDA(agentId, 'devnet');
420
+ const [linkedPDA] = getLinkedWalletPDA(genesisPDA, wallet, 'devnet');
421
+ const [namePDA] = getNameRegistryPDA('brainChain', 'devnet');
422
+ console.log(`\n Name Registry: ${namePDA.toBase58()}`);
423
+ console.log(` Linked Wallet: ${linkedPDA.toBase58()}`);
424
+
425
+ // Hash functions (deterministic, no RPC)
426
+ const hash1 = hashAgentId('brainChain');
427
+ const hash2 = hashAgentId('brainChain');
428
+ console.log(`\n Hash stable: ${hash1.equals(hash2) ? 'YES ✓' : 'NO ✗'}`);
429
+ console.log(` Hash hex: ${hash1.toString('hex').slice(0, 32)}...`);
430
+
431
+ console.log('\n✅ All derivations are deterministic and offline');
432
+ }
433
+
434
+ // ═══════════════════════════════════════════════════════
435
+ // PATTERN 9: Name Availability Check
436
+ // Use case: Registration form, name suggestions
437
+ // ═══════════════════════════════════════════════════════
438
+
439
+ async function nameAvailability() {
440
+ const sdk = new SATPV3SDK({ network: 'devnet' });
441
+
442
+ console.log('=== Name Availability Check ===\n');
443
+
444
+ const names = ['brainChain', 'available-name-12345', 'BrainChain']; // Note: case-insensitive
445
+
446
+ for (const name of names) {
447
+ const available = await sdk.isNameAvailable(name);
448
+ const [pda] = getNameRegistryPDA(name, 'devnet');
449
+ console.log(` "${name}" → ${available ? '✅ Available' : '❌ Taken'} (PDA: ${pda.toBase58().slice(0, 12)}...)`);
450
+ }
451
+
452
+ console.log('\nNote: Names are case-insensitive.');
453
+ console.log('"brainChain" and "BrainChain" hash to the same PDA.');
454
+
455
+ console.log('\n✅ Name check demonstrated');
456
+ }
457
+
458
+ // ═══════════════════════════════════════════════════════
459
+ // CLI Runner
460
+ // ═══════════════════════════════════════════════════════
461
+
462
+ const examples = {
463
+ 'lookup': () => lookupAgent(process.argv[3] || 'brainChain'),
464
+ 'register': () => registerAgent(),
465
+ 'review-flow': () => reviewAndReputationFlow(),
466
+ 'reputation-flow': () => reviewAndReputationFlow(),
467
+ 'attestation-flow': () => attestationAndValidationFlow(),
468
+ 'migration': () => migrationFlow(),
469
+ 'batch-lookup': () => batchLookup(),
470
+ 'authority-rotation': () => authorityRotation(),
471
+ 'pda-derivation': () => offlinePDADerivation(),
472
+ 'name-check': () => nameAvailability(),
473
+ };
474
+
475
+ async function main() {
476
+ const example = process.argv[2];
477
+
478
+ if (!example || !examples[example]) {
479
+ console.log('SATP V3 SDK — Integration Patterns\n');
480
+ console.log('Usage: node integration-patterns.js <example>\n');
481
+ console.log('Available examples:');
482
+ Object.keys(examples).forEach(name => console.log(` ${name}`));
483
+ console.log('\nExample: node integration-patterns.js lookup brainChain');
484
+ return;
485
+ }
486
+
487
+ try {
488
+ await examples[example]();
489
+ } catch (err) {
490
+ console.error(`Error: ${err.message}`);
491
+ process.exit(1);
492
+ }
493
+ }
494
+
495
+ main();