@brainai/satp-client 2.0.0 → 2.0.2
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 +529 -51
- package/examples/integration-patterns.js +495 -0
- package/examples/runtime-policy-adapter.js +75 -0
- package/examples/x402-discovery-evidence-lookup.js +66 -0
- package/package.json +42 -5
- package/src/attestation-request.js +149 -0
- package/src/borsh-reader.d.ts +213 -0
- package/src/borsh-reader.js +587 -0
- package/src/constants.js +10 -0
- package/src/index.d.ts +507 -0
- package/src/index.js +672 -0
- package/src/pda.js +43 -0
- package/src/runtime-policy-adapter.js +206 -0
- package/src/trust-packet.js +148 -0
- package/src/v3-pda.d.ts +72 -0
- package/src/v3-pda.js +281 -0
- package/src/v3-sdk.d.ts +508 -0
- package/src/v3-sdk.js +1800 -0
- package/src/wallet-control-challenge.js +351 -0
- package/src/x402-discovery.js +180 -0
package/README.md
CHANGED
|
@@ -1,82 +1,560 @@
|
|
|
1
|
-
# SATP
|
|
1
|
+
# SATP V3 SDK - `@brainai/satp-client`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Solana Agent Token Protocol** - JavaScript/TypeScript SDK for interacting with the SATP V3 devnet programs.
|
|
4
4
|
|
|
5
|
-
|
|
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
|
+
|
|
7
|
+
## Installation
|
|
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:
|
|
6
44
|
|
|
7
45
|
```bash
|
|
8
|
-
|
|
46
|
+
npm install git+https://github.com/brainAI-bot/satp.git#<SATP_COMMIT>
|
|
9
47
|
```
|
|
10
48
|
|
|
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.
|
|
51
|
+
|
|
52
|
+
Mainnet program IDs are intentionally not enabled in this release candidate.
|
|
53
|
+
Constructors and helpers fail closed for `network: 'mainnet'` until an approved
|
|
54
|
+
mainnet decision packet provides production program IDs.
|
|
55
|
+
|
|
56
|
+
**Runtime dependency:** `@solana/web3.js ^1.98.4`
|
|
57
|
+
|
|
11
58
|
## Quick Start
|
|
12
59
|
|
|
13
|
-
```
|
|
14
|
-
const {
|
|
60
|
+
```javascript
|
|
61
|
+
const { SATPV3SDK } = require('@brainai/satp-client');
|
|
62
|
+
|
|
63
|
+
// Initialize (devnet by default)
|
|
64
|
+
const sdk = new SATPV3SDK({ network: 'devnet' });
|
|
65
|
+
|
|
66
|
+
// Check if an agent has an identity
|
|
67
|
+
const exists = await sdk.hasIdentity('brainChain');
|
|
68
|
+
console.log(exists); // true
|
|
69
|
+
|
|
70
|
+
// Read a Genesis Record
|
|
71
|
+
const record = await sdk.getGenesisRecord('brainChain');
|
|
72
|
+
console.log(record.agentName, record.category, record.isActive);
|
|
73
|
+
|
|
74
|
+
// Build a transaction (unsigned — sign with your wallet)
|
|
75
|
+
const tx = await sdk.buildCreateIdentity(creatorPubkey, 'myAgent', {
|
|
76
|
+
agentName: 'My Agent',
|
|
77
|
+
description: 'An AI agent on Solana',
|
|
78
|
+
category: 'assistant',
|
|
79
|
+
capabilities: ['chat', 'code'],
|
|
80
|
+
metadataUri: 'https://example.com/meta.json',
|
|
81
|
+
});
|
|
82
|
+
// Sign and send tx with your wallet...
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Architecture
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
89
|
+
│ SATP V3 SDK │
|
|
90
|
+
├─────────────┬──────────────┬──────────────┬─────────────────────┤
|
|
91
|
+
│ Identity │ Reviews │ Attestations │ Escrow │
|
|
92
|
+
│ (20 methods)│ (7 methods) │ (3 methods) │ (10 methods) │
|
|
93
|
+
├─────────────┼──────────────┼──────────────┤ │
|
|
94
|
+
│ Reputation │ Validation │ Migration │ │
|
|
95
|
+
│ (1 method) │ (1 method) │ (1 method) │ │
|
|
96
|
+
├─────────────┴──────────────┴──────────────┴─────────────────────┤
|
|
97
|
+
│ PDA Derivation │ Borsh Serialization │ RPC Helpers │
|
|
98
|
+
└─────────────────┴───────────────────────┴───────────────────────┘
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Programs & Program IDs
|
|
102
|
+
|
|
103
|
+
| Program | Devnet | Description |
|
|
104
|
+
|---------|--------|-------------|
|
|
105
|
+
| `identity_v3` | `GTppU4E44BqXTQgbqMZ68ozFzhP1TLty3EGnzzjtNZfG` | Agent identity, names, wallets, face/birth |
|
|
106
|
+
| `reviews_v3` | `r9XX4frcqxxAZ6Au9V5PA3EAxs1zoNckqLLmoSRcNr4` | Peer reviews with 1-5 star ratings |
|
|
107
|
+
| `attestations_v3` | `6Xd1dAQJPvQRJ4Ntr6LtPTjDjPUZ8nfnmYLZaZ2DtrdD` | Third-party attestations & proofs |
|
|
108
|
+
| `reputation_v3` | `2Lz7KzMvKdrGeAuS8WPHu7jK2yScrnKVgacpYVEuDjkJ` | Weighted reputation scoring (CPI → identity) |
|
|
109
|
+
| `validation_v3` | `6rYRiCYidJYV7QvKrzKGgNu4oMh6BAvynked69R7xMbV` | Validation level computation (CPI → identity) |
|
|
110
|
+
| `escrow_v3` | `HXCUWKR2NvRcZ7rNAJHwPcH6QAAWaLR4bRFbfyuDND6C` | SOL escrow for agent jobs |
|
|
111
|
+
|
|
112
|
+
## API Reference
|
|
113
|
+
|
|
114
|
+
### Read-only Trust Packet Helpers
|
|
115
|
+
|
|
116
|
+
`buildSatpTrustPacket(opts)` creates a deterministic, offline trust packet for
|
|
117
|
+
consumer preflight and release-packet review. It uses the same inputs as
|
|
118
|
+
`prepareIdentityAttestationRequest`, then includes the derived program IDs,
|
|
119
|
+
Genesis PDA, attestation PDA, request hash, and the unsigned request object.
|
|
120
|
+
The packet is intentionally read-only: `flags.signingRequired`,
|
|
121
|
+
`flags.transactionRequired`, `flags.writesRequired`, and
|
|
122
|
+
`flags.livePaymentRequired` are all `false`; `instructions` and `signers`
|
|
123
|
+
are empty; and `transaction` is `null`.
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
const {
|
|
127
|
+
buildSatpTrustPacket,
|
|
128
|
+
validateSatpTrustPacket,
|
|
129
|
+
} = require('@brainai/satp-client');
|
|
130
|
+
|
|
131
|
+
const trustPacket = buildSatpTrustPacket({
|
|
132
|
+
subjectWallet: '11111111111111111111111111111111',
|
|
133
|
+
agentId: 'brainChain',
|
|
134
|
+
claimType: 'identity',
|
|
135
|
+
metadataHash: '93d122f8879fe87c186c10a00db8fbc80a73cecd2ede44b9ffa6410be3c2b805',
|
|
136
|
+
network: 'devnet',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const validation = validateSatpTrustPacket(trustPacket);
|
|
140
|
+
if (!validation.ok) throw new Error(validation.errors.join('; '));
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`validateSatpTrustPacket(packet)` returns `{ ok, errors }`. Validation requires
|
|
144
|
+
`packetType: 'satp-trust-packet'` and
|
|
145
|
+
`mode: 'offline-readonly-trust-packet'`, rejects changed read-only flags, and
|
|
146
|
+
re-derives the expected packet so tampered PDA, program, request, or hash fields
|
|
147
|
+
surface as explicit errors.
|
|
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
|
+
|
|
212
|
+
### Constructor
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const sdk = new SATPV3SDK({ network, rpcUrl });
|
|
216
|
+
// network: 'devnet' (default). 'mainnet' fails closed until approved IDs exist.
|
|
217
|
+
// rpcUrl: optional custom RPC endpoint
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### Identity Methods (20)
|
|
223
|
+
|
|
224
|
+
| Method | Description |
|
|
225
|
+
|--------|-------------|
|
|
226
|
+
| `buildCreateIdentity(creator, agentId, meta)` | Create a new agent identity (Genesis Record) |
|
|
227
|
+
| `buildBurnToBecome(authority, agentId, faceImage, faceMint, faceBurnTx)` | Burn NFT to set agent's face (birth ritual) |
|
|
228
|
+
| `buildUpdateIdentity(authority, agentId, updates)` | Update mutable fields (description, capabilities, metadata) |
|
|
229
|
+
| `buildProposeAuthority(authority, agentId, newAuthority)` | Propose authority transfer (2-step) |
|
|
230
|
+
| `buildAcceptAuthority(newAuthority, agentId)` | Accept proposed authority transfer |
|
|
231
|
+
| `buildCancelAuthorityTransfer(authority, agentId)` | Cancel pending authority transfer |
|
|
232
|
+
| `buildRegisterName(authority, agentId, name)` | Register a unique name for an agent |
|
|
233
|
+
| `buildReleaseName(authority, agentId, name)` | Release a registered name |
|
|
234
|
+
| `buildLinkWallet(authority, agentId, wallet, chain, label)` | Link an external wallet to identity |
|
|
235
|
+
| `buildUnlinkWallet(authority, agentId, wallet)` | Unlink an external wallet |
|
|
236
|
+
| `buildInitMintTracker(authority, agentId)` | Initialize NFT mint tracker |
|
|
237
|
+
| `buildRecordMint(authority, agentId)` | Record an NFT mint event |
|
|
238
|
+
| `buildDeactivateIdentity(authority, agentId)` | Deactivate an identity |
|
|
239
|
+
| `buildReactivateIdentity(authority, agentId)` | Reactivate a deactivated identity |
|
|
240
|
+
| `getGenesisRecord(agentId)` | Read a Genesis Record from chain |
|
|
241
|
+
| `hasIdentity(agentId)` | Check if an agent has an identity |
|
|
242
|
+
| `getEscrowPDA(client, description, nonce)` | Derive escrow PDA (sync) |
|
|
243
|
+
| `buildMigrateV2ToV3(v2Authority, agentId, meta)` | Migrate from V2 to V3 identity |
|
|
244
|
+
|
|
245
|
+
#### Genesis Record Fields
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
const record = await sdk.getGenesisRecord('brainChain');
|
|
249
|
+
// Returns:
|
|
250
|
+
{
|
|
251
|
+
agentIdHash: string, // SHA-256 of agent_id
|
|
252
|
+
agentName: string, // Display name
|
|
253
|
+
description: string, // Agent description
|
|
254
|
+
category: string, // e.g. "developer", "assistant"
|
|
255
|
+
capabilities: string[], // e.g. ["solana", "code"]
|
|
256
|
+
metadataUri: string, // Off-chain metadata URL
|
|
257
|
+
faceImage: string, // Face image URL (after birth)
|
|
258
|
+
faceMint: string, // NFT mint address (after birth)
|
|
259
|
+
faceBurnTx: string, // Burn transaction signature
|
|
260
|
+
genesisRecord: number, // Unix timestamp of birth
|
|
261
|
+
isBorn: boolean, // Whether agent has completed birth ritual
|
|
262
|
+
isActive: boolean, // Whether identity is active
|
|
263
|
+
authority: string, // Current authority pubkey
|
|
264
|
+
pendingAuthority: string | null,
|
|
265
|
+
reputationScore: number, // CPI-updated reputation
|
|
266
|
+
validationLevel: number, // CPI-updated validation
|
|
267
|
+
createdAt: number, // Unix timestamp
|
|
268
|
+
updatedAt: number, // Unix timestamp
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### Reviews Methods (7)
|
|
15
275
|
|
|
16
|
-
|
|
17
|
-
|
|
276
|
+
| Method | Description |
|
|
277
|
+
|--------|-------------|
|
|
278
|
+
| `buildInitReviewCounter(payer, agentId)` | Initialize review counter for an agent |
|
|
279
|
+
| `buildCreateReview(reviewer, agentId, rating, text, metadata, opts)` | Create a 1-5 star review |
|
|
280
|
+
| `buildCreateReviewWithSelfCheck(reviewer, agentId, rating, text, metadata)` | Create review with self-review prevention |
|
|
281
|
+
| `buildUpdateReview(reviewer, reviewPDA, updates)` | Update an existing review |
|
|
282
|
+
| `buildDeleteReview(reviewer, reviewPDA)` | Soft-delete a review |
|
|
283
|
+
| `getReview(agentId, reviewer)` | Read a review from chain |
|
|
284
|
+
| `getReviewCount(agentId)` | Get total review count for an agent |
|
|
18
285
|
|
|
19
|
-
|
|
20
|
-
|
|
286
|
+
```javascript
|
|
287
|
+
// Create a review
|
|
288
|
+
const tx = await sdk.buildCreateReview(
|
|
289
|
+
reviewerPubkey,
|
|
290
|
+
'brainChain', // agent being reviewed
|
|
291
|
+
5, // rating (1-5)
|
|
292
|
+
'Excellent Solana dev',
|
|
293
|
+
'metadata',
|
|
294
|
+
{ category: 'development' }
|
|
295
|
+
);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### Attestations Methods (3)
|
|
301
|
+
|
|
302
|
+
| Method | Description |
|
|
303
|
+
|--------|-------------|
|
|
304
|
+
| `buildCreateAttestation(issuer, agentId, type, proofData, expiresAt)` | Issue an attestation |
|
|
305
|
+
| `buildVerifyAttestation(issuer, attestationPDA)` | Mark attestation as verified |
|
|
306
|
+
| `buildRevokeAttestation(issuer, attestationPDA)` | Revoke an attestation |
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
// Issue a KYC attestation
|
|
310
|
+
const tx = await sdk.buildCreateAttestation(
|
|
311
|
+
issuerPubkey,
|
|
312
|
+
'brainChain',
|
|
313
|
+
'kyc', // attestation type
|
|
314
|
+
'proof-hash-here',
|
|
315
|
+
Math.floor(Date.now()/1000) + 86400 * 365 // expires in 1 year
|
|
316
|
+
);
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### Reputation & Validation Methods (2)
|
|
322
|
+
|
|
323
|
+
| Method | Description |
|
|
324
|
+
|--------|-------------|
|
|
325
|
+
| `buildRecomputeReputation(caller, agentId, reviewAccounts)` | Recompute reputation score from reviews (CPI → identity) |
|
|
326
|
+
| `buildRecomputeLevel(caller, agentId, attestationAccounts)` | Recompute validation level from attestations (CPI → identity) |
|
|
327
|
+
|
|
328
|
+
These use Cross-Program Invocation to update fields directly on the Genesis Record.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
### Escrow Methods (10)
|
|
333
|
+
|
|
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.
|
|
337
|
+
|
|
338
|
+
| Method | Description |
|
|
339
|
+
|--------|-------------|
|
|
340
|
+
| `buildCreateEscrow(client, agentWallet, agentId, amount, description, deadline, nonce, opts)` | Create SOL escrow for a job |
|
|
341
|
+
| `buildSubmitWork(agent, escrowPDA, workProof)` | Agent submits work proof |
|
|
342
|
+
| `buildEscrowRelease(client, agent, escrowPDA)` | Client releases full payment |
|
|
343
|
+
| `buildPartialRelease(client, agent, escrowPDA, amount)` | Client releases partial payment |
|
|
344
|
+
| `buildCancelEscrow(client, escrowPDA)` | Cancel escrow (refund client) |
|
|
345
|
+
| `buildRaiseDispute(signer, escrowPDA, reason)` | Raise a dispute |
|
|
346
|
+
| `buildResolveDispute(arbiter, agent, client, escrowPDA, agentAmt, clientAmt)` | Arbiter resolves dispute |
|
|
347
|
+
| `buildExtendDeadline(client, escrowPDA, newDeadline)` | Extend job deadline |
|
|
348
|
+
| `buildCloseEscrow(client, escrowPDA)` | Close completed/cancelled escrow (reclaim rent) |
|
|
349
|
+
| `getEscrow(escrowPDA)` | Read escrow state from chain |
|
|
350
|
+
|
|
351
|
+
#### Escrow Lifecycle
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
Created → WorkSubmitted → Released (full or partial)
|
|
355
|
+
↓ ↓ ↓
|
|
356
|
+
Cancelled Disputed Closed (rent reclaimed)
|
|
357
|
+
↓
|
|
358
|
+
Resolved (split)
|
|
359
|
+
↓
|
|
360
|
+
Closed
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
// Create an escrow (0.5 SOL for a generic work agreement)
|
|
365
|
+
const tx = await sdk.buildCreateEscrow(
|
|
366
|
+
clientPubkey,
|
|
367
|
+
agentWallet,
|
|
368
|
+
'brainChain',
|
|
369
|
+
0.5 * 1e9, // lamports
|
|
370
|
+
'Complete agreed work',
|
|
371
|
+
Math.floor(Date.now()/1000) + 86400 * 7, // 7 day deadline
|
|
372
|
+
0, // nonce (for multiple escrows with same description)
|
|
373
|
+
{ arbiter: arbiterPubkey }
|
|
374
|
+
);
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
### PDA Helpers (exported from `v3-pda.js`)
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
const {
|
|
383
|
+
hashAgentId, // SHA-256 hash of agent_id string
|
|
384
|
+
hashName, // SHA-256 hash of name string
|
|
385
|
+
getGenesisPDA, // [b"genesis_record", agent_id_hash]
|
|
386
|
+
getNameRegistryPDA, // [b"name_registry_v3", name_hash]
|
|
387
|
+
getLinkedWalletPDA, // [b"linked_wallet_v3", agent_id_hash, wallet]
|
|
388
|
+
getV3MintTrackerPDA, // [b"mint_tracker_v3", agent_id_hash]
|
|
389
|
+
getV3ReviewPDA, // [b"review_v3", agent_id_hash, reviewer]
|
|
390
|
+
getV3ReviewCounterPDA, // [b"review_counter_v3", agent_id_hash]
|
|
391
|
+
getV3AttestationPDA, // [b"attestation_v3", agent_id_hash, issuer, type_hash]
|
|
392
|
+
getV3ReputationAuthorityPDA, // [b"reputation_authority", agent_id_hash]
|
|
393
|
+
getV3ValidationAuthorityPDA, // [b"validation_authority", agent_id_hash]
|
|
394
|
+
getV3EscrowPDA, // [b"escrow_v3", client, desc_hash, nonce_le]
|
|
395
|
+
getV3ProgramIds, // Returns all 6 program IDs for network
|
|
396
|
+
} = require('@brainai/satp-client/src/v3-pda');
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
### Escrow SDK Utilities (exported from `v3-sdk.js`)
|
|
402
|
+
|
|
403
|
+
```javascript
|
|
404
|
+
const {
|
|
405
|
+
deriveEscrowPda, // Derive escrow PDA from params
|
|
406
|
+
descriptionHash, // SHA-256 hash of description string
|
|
407
|
+
EscrowStatus, // Enum: { Active: 0, WorkSubmitted: 1, Released: 2, Cancelled: 3, Disputed: 4, Resolved: 5 }
|
|
408
|
+
escrowStatusLabel, // Convert status number to human-readable string
|
|
409
|
+
escrowRemaining, // Calculate remaining escrow balance
|
|
410
|
+
isEscrowExpired, // Check if escrow has passed deadline
|
|
411
|
+
} = require('@brainai/satp-client/src/v3-sdk');
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Transaction Pattern
|
|
415
|
+
|
|
416
|
+
All `build*` methods return an **unsigned** `Transaction` object. Your application is responsible for:
|
|
21
417
|
|
|
22
|
-
|
|
23
|
-
|
|
418
|
+
1. Setting `recentBlockhash` and `feePayer`
|
|
419
|
+
2. Signing with the appropriate wallet
|
|
420
|
+
3. Sending to the network
|
|
24
421
|
|
|
25
|
-
|
|
26
|
-
const
|
|
422
|
+
```javascript
|
|
423
|
+
const tx = await sdk.buildCreateIdentity(wallet.publicKey, 'myAgent', { ... });
|
|
424
|
+
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
|
|
425
|
+
tx.feePayer = wallet.publicKey;
|
|
426
|
+
tx.sign(wallet);
|
|
427
|
+
const sig = await connection.sendRawTransaction(tx.serialize());
|
|
428
|
+
await connection.confirmTransaction(sig);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Consumer APIs
|
|
432
|
+
|
|
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.
|
|
27
436
|
|
|
28
|
-
|
|
29
|
-
|
|
437
|
+
## Testing
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Unit tests (101)
|
|
441
|
+
node test-v3.js
|
|
442
|
+
|
|
443
|
+
# Devnet integration tests (16)
|
|
444
|
+
node test-v3-devnet.js
|
|
445
|
+
|
|
446
|
+
# CPI integration tests (35)
|
|
447
|
+
cd .. && node tests/devnet-cpi-integration.js
|
|
448
|
+
|
|
449
|
+
# Release-safety defaults and mainnet fail-closed checks
|
|
450
|
+
node test-release-safety.js
|
|
30
451
|
```
|
|
31
452
|
|
|
32
|
-
##
|
|
453
|
+
## Network Configuration
|
|
454
|
+
|
|
455
|
+
```javascript
|
|
456
|
+
// Devnet (default)
|
|
457
|
+
const sdk = new SATPV3SDK();
|
|
458
|
+
|
|
459
|
+
// Explicit devnet
|
|
460
|
+
const sdk = new SATPV3SDK({ network: 'devnet' });
|
|
461
|
+
|
|
462
|
+
// Custom RPC
|
|
463
|
+
const sdk = new SATPV3SDK({ rpcUrl: 'https://my-rpc.example.com' });
|
|
464
|
+
|
|
465
|
+
// Mainnet currently fails closed until approved program IDs are configured
|
|
466
|
+
assert.throws(() => new SATPV3SDK({ network: 'mainnet' }));
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Borsh Deserialization Helpers (v3.6.0)
|
|
470
|
+
|
|
471
|
+
Zero-dependency Borsh deserialization for all 8 SATP V3 account types. Decode raw on-chain data without the `borsh` library.
|
|
472
|
+
|
|
473
|
+
### Supported Account Types
|
|
474
|
+
|
|
475
|
+
| Account | Program | Deserializer |
|
|
476
|
+
|---------|---------|-------------|
|
|
477
|
+
| GenesisRecord | Identity V3 | `deserializeGenesisRecord(data)` |
|
|
478
|
+
| LinkedWallet | Identity V3 | `deserializeLinkedWallet(data)` |
|
|
479
|
+
| MintTracker | Identity V3 | `deserializeMintTracker(data)` |
|
|
480
|
+
| NameRegistry | Identity V3 | `deserializeNameRegistry(data)` |
|
|
481
|
+
| Review | Reviews V3 | `deserializeReview(data)` |
|
|
482
|
+
| ReviewCounter | Reviews V3 | `deserializeReviewCounter(data)` |
|
|
483
|
+
| Attestation | Attestations V3 | `deserializeAttestation(data)` |
|
|
484
|
+
| EscrowV3 | Escrow V3 | `deserializeEscrowV3(data)` |
|
|
485
|
+
|
|
486
|
+
### Usage: Typed Deserialization
|
|
33
487
|
|
|
34
488
|
```js
|
|
35
|
-
const {
|
|
489
|
+
const { deserializeGenesisRecord, deserializeAttestation } = require('@brainai/satp-client');
|
|
490
|
+
const { Connection, PublicKey } = require('@solana/web3.js');
|
|
491
|
+
|
|
492
|
+
const conn = new Connection('https://api.devnet.solana.com');
|
|
493
|
+
|
|
494
|
+
// Fetch raw account and deserialize
|
|
495
|
+
const acct = await conn.getAccountInfo(new PublicKey('...'));
|
|
496
|
+
const genesis = deserializeGenesisRecord(acct.data);
|
|
497
|
+
console.log(genesis.agentName, genesis.reputationScore, genesis.isBorn);
|
|
498
|
+
```
|
|
36
499
|
|
|
37
|
-
|
|
500
|
+
### Usage: Auto-detect Account Type
|
|
38
501
|
|
|
39
|
-
|
|
40
|
-
const
|
|
502
|
+
```js
|
|
503
|
+
const { deserializeAccount } = require('@brainai/satp-client');
|
|
41
504
|
|
|
42
|
-
//
|
|
43
|
-
const
|
|
505
|
+
// Automatically detects type from 8-byte Anchor discriminator
|
|
506
|
+
const { type, data } = deserializeAccount(acct.data);
|
|
507
|
+
console.log(type); // "GenesisRecord" | "Attestation" | "EscrowV3" | ...
|
|
508
|
+
console.log(data); // Fully parsed object
|
|
44
509
|
```
|
|
45
510
|
|
|
46
|
-
|
|
511
|
+
### Usage: Batch Deserialization (getProgramAccounts)
|
|
47
512
|
|
|
48
513
|
```js
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
);
|
|
53
|
-
//
|
|
514
|
+
const { deserializeBatch, DISCRIMINATORS } = require('@brainai/satp-client');
|
|
515
|
+
|
|
516
|
+
const accounts = await conn.getProgramAccounts(REVIEWS_PROGRAM_ID);
|
|
517
|
+
const reviews = deserializeBatch(accounts, 'Review');
|
|
518
|
+
// [{ pubkey: "...", type: "Review", data: { agentId, rating, ... } }, ...]
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Usage: BorshReader (Custom Deserialization)
|
|
522
|
+
|
|
523
|
+
```js
|
|
524
|
+
const { BorshReader } = require('@brainai/satp-client');
|
|
525
|
+
|
|
526
|
+
// Low-level reader for custom account layouts
|
|
527
|
+
const r = new BorshReader(acct.data);
|
|
528
|
+
r.skipDiscriminator(); // skip 8-byte Anchor discriminator
|
|
529
|
+
const hash = r.readFixedBytes32(); // [u8; 32]
|
|
530
|
+
const name = r.readString(); // Borsh String
|
|
531
|
+
const items = r.readVecString(); // Vec<String>
|
|
532
|
+
const pk = r.readPubkeyBase58(); // Pubkey → base58
|
|
533
|
+
const opt = r.readOptionI64(); // Option<i64> → number | null
|
|
54
534
|
```
|
|
55
535
|
|
|
56
|
-
|
|
536
|
+
### Discriminator Utilities
|
|
57
537
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
538
|
+
```js
|
|
539
|
+
const { isAccountType, getAccountDiscriminator, DISCRIMINATORS } = require('@brainai/satp-client');
|
|
540
|
+
|
|
541
|
+
// Check account type before deserializing
|
|
542
|
+
if (isAccountType(acct.data, 'EscrowV3')) {
|
|
543
|
+
const escrow = deserializeEscrowV3(acct.data);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Get discriminator for filtering
|
|
547
|
+
const disc = getAccountDiscriminator('Attestation'); // 8-byte Buffer
|
|
548
|
+
// Use with getProgramAccounts memcmp filter
|
|
549
|
+
```
|
|
68
550
|
|
|
69
|
-
##
|
|
551
|
+
## Security
|
|
70
552
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
| Validation | `AdDWFa9oEmZdrTrhu8YTWu4ozbTP7e6qa9rvyqfAvM7N` |
|
|
76
|
-
| Escrow | `STyY8w4ZHws3X1AMoocWuDYBoogVDwvymPy8Wifx5TH` |
|
|
553
|
+
- All transactions are returned **unsigned** — the SDK never holds private keys
|
|
554
|
+
- PDA derivation is deterministic and verified against on-chain seeds
|
|
555
|
+
- CPI boundaries enforce program-level authorization
|
|
556
|
+
- Escrow funds are held by PDA-owned accounts (no custodial risk)
|
|
77
557
|
|
|
78
|
-
##
|
|
558
|
+
## License
|
|
79
559
|
|
|
80
|
-
|
|
81
|
-
- Write operations require SOL for transaction fees + rent.
|
|
82
|
-
- Always test on devnet first: `new SATPSDK({ rpcUrl: 'https://api.devnet.solana.com' })`
|
|
560
|
+
MIT — brainAI 2026
|