@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/package.json CHANGED
@@ -1,14 +1,37 @@
1
1
  {
2
2
  "name": "@brainai/satp-client",
3
- "version": "2.0.0",
4
- "description": "SATP v2 Client SDK Solana Agent Token Protocol (Identity, Reviews, Reputation, Attestations, Validation)",
3
+ "version": "2.0.2",
4
+ "description": "SATP client SDK surface prepared for release-candidate review; publish only after the release packet passes.",
5
5
  "main": "src/index.js",
6
+ "types": "src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "default": "./src/index.js"
11
+ },
12
+ "./wallet-control-challenge": {
13
+ "types": "./src/index.d.ts",
14
+ "default": "./src/wallet-control-challenge.js"
15
+ },
16
+ "./x402-discovery": {
17
+ "types": "./src/index.d.ts",
18
+ "default": "./src/x402-discovery.js"
19
+ },
20
+ "./src/*": "./src/*",
21
+ "./package.json": "./package.json"
22
+ },
6
23
  "files": [
7
24
  "src/",
25
+ "examples/",
8
26
  "README.md"
9
27
  ],
10
28
  "scripts": {
11
- "test": "node test.js"
29
+ "test": "node test.js",
30
+ "test:runtime-policy": "node --test test-runtime-policy-adapter.js",
31
+ "runtime-policy:example": "node examples/runtime-policy-adapter.js",
32
+ "test:wallet-control": "node test-wallet-control-challenge.js",
33
+ "test:x402-discovery": "node test-x402-discovery.js",
34
+ "check:exports": "node test-release-safety.js"
12
35
  },
13
36
  "keywords": [
14
37
  "solana",
@@ -23,12 +46,26 @@
23
46
  "license": "MIT",
24
47
  "repository": {
25
48
  "type": "git",
26
- "url": "https://github.com/brainai-dev/satp-client"
49
+ "url": "git+https://github.com/brainAI-bot/satp.git"
50
+ },
51
+ "homepage": "https://github.com/brainAI-bot/satp",
52
+ "engines": {
53
+ "node": ">=20.18"
27
54
  },
28
- "homepage": "https://agentfolio.bot",
29
55
  "dependencies": {
30
56
  "@solana/web3.js": "^1.98.4",
31
57
  "borsh": "^2.0.0",
32
58
  "bs58": "^6.0.0"
59
+ },
60
+ "private": false,
61
+ "overrides": {
62
+ "ws": "^8.21.0",
63
+ "jayson": {
64
+ "uuid": "^11.1.1"
65
+ }
66
+ },
67
+ "publishConfig": {
68
+ "tag": "rc",
69
+ "access": "public"
33
70
  }
34
71
  }
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const { PublicKey } = require('@solana/web3.js');
5
+ const {
6
+ getV3ProgramIds,
7
+ hashAgentId,
8
+ getGenesisPDA,
9
+ getV3AttestationPDA,
10
+ } = require('./v3-pda');
11
+
12
+ const DEFAULT_NETWORK = 'devnet';
13
+ const DEFAULT_ATTESTER = '11111111111111111111111111111111';
14
+ const REQUEST_SCHEMA_VERSION = 'satp.identityAttestationRequest.v1';
15
+
16
+ function normalizeNetwork(network = DEFAULT_NETWORK) {
17
+ if (network !== 'devnet' && network !== 'mainnet') {
18
+ throw new Error('Invalid network: expected devnet or mainnet');
19
+ }
20
+ return network;
21
+ }
22
+
23
+ function normalizePublicKey(value, field) {
24
+ try {
25
+ return new PublicKey(value).toBase58();
26
+ } catch (err) {
27
+ throw new Error(`Invalid ${field}: expected a Solana public key`);
28
+ }
29
+ }
30
+
31
+ function normalizeString(value, field, { maxBytes } = {}) {
32
+ if (typeof value !== 'string' || value.trim() === '') {
33
+ throw new Error(`Invalid ${field}: expected a non-empty string`);
34
+ }
35
+ const normalized = value.trim();
36
+ if (maxBytes && Buffer.byteLength(normalized, 'utf8') > maxBytes) {
37
+ throw new Error(`Invalid ${field}: expected at most ${maxBytes} UTF-8 bytes`);
38
+ }
39
+ return normalized;
40
+ }
41
+
42
+ function normalizeMetadataHash(metadataHash) {
43
+ if (typeof metadataHash !== 'string' || !/^[a-fA-F0-9]{64}$/.test(metadataHash)) {
44
+ throw new Error('Invalid metadataHash: expected a 32-byte hex string');
45
+ }
46
+ return metadataHash.toLowerCase();
47
+ }
48
+
49
+ function normalizeExpiresAt(expiresAt) {
50
+ if (expiresAt === undefined || expiresAt === null) return null;
51
+ if (!Number.isSafeInteger(expiresAt) || expiresAt < 0) {
52
+ throw new Error('Invalid expiresAt: expected a non-negative safe integer Unix timestamp');
53
+ }
54
+ return expiresAt;
55
+ }
56
+
57
+ function publicProgramIds(network) {
58
+ const ids = getV3ProgramIds(network);
59
+ return {
60
+ identity: ids.IDENTITY.toBase58(),
61
+ attestations: ids.ATTESTATIONS.toBase58(),
62
+ };
63
+ }
64
+
65
+ function canonicalStringify(value) {
66
+ if (Array.isArray(value)) {
67
+ return `[${value.map(canonicalStringify).join(',')}]`;
68
+ }
69
+ if (value && typeof value === 'object') {
70
+ const body = Object.keys(value)
71
+ .sort()
72
+ .map((key) => `${JSON.stringify(key)}:${canonicalStringify(value[key])}`)
73
+ .join(',');
74
+ return `{${body}}`;
75
+ }
76
+ return JSON.stringify(value);
77
+ }
78
+
79
+ function hashObject(value) {
80
+ return crypto.createHash('sha256').update(canonicalStringify(value), 'utf8').digest('hex');
81
+ }
82
+
83
+ /**
84
+ * Prepare a deterministic, offline, unsigned identity-attestation request.
85
+ *
86
+ * This helper derives request metadata only. It does not connect to RPC, read a
87
+ * credentials, build a transaction, sign, send, or mutate chain state.
88
+ *
89
+ * @param {object} opts
90
+ * @param {string|PublicKey} opts.subjectWallet - Wallet controlled by the attested identity.
91
+ * @param {string} [opts.agentId=subjectWallet] - SATP agent identifier used for V3 PDA seeds.
92
+ * @param {string} [opts.claimType] - Human-readable attestation type.
93
+ * @param {string} [opts.attestationType=claimType] - Alias for claimType.
94
+ * @param {string} opts.metadataHash - 32-byte hex SHA-256 hash of the off-chain metadata/proof.
95
+ * @param {string|PublicKey} [opts.attester] - Attester/issuer public key.
96
+ * @param {'devnet'|'mainnet'} [opts.network='devnet']
97
+ * @param {number|null} [opts.expiresAt=null] - Optional Unix timestamp for downstream signing.
98
+ * @returns {object} Plain JSON-safe request metadata.
99
+ */
100
+ function prepareIdentityAttestationRequest(opts = {}) {
101
+ if (!opts || typeof opts !== 'object') {
102
+ throw new Error('Invalid opts: expected an options object');
103
+ }
104
+ const network = normalizeNetwork(opts.network);
105
+ const subjectWallet = normalizePublicKey(opts.subjectWallet, 'subjectWallet');
106
+ const agentId = normalizeString(opts.agentId || subjectWallet, 'agentId');
107
+ const claimType = normalizeString(opts.claimType || opts.attestationType, 'claimType', { maxBytes: 32 });
108
+ const metadataHash = normalizeMetadataHash(opts.metadataHash);
109
+ const attester = normalizePublicKey(opts.attester || opts.issuer || DEFAULT_ATTESTER, 'attester');
110
+ const expiresAt = normalizeExpiresAt(opts.expiresAt);
111
+ const agentIdHash = hashAgentId(agentId).toString('hex');
112
+ const [genesisPda, genesisBump] = getGenesisPDA(Buffer.from(agentIdHash, 'hex'), network);
113
+ const [attestationPda, attestationBump] = getV3AttestationPDA(Buffer.from(agentIdHash, 'hex'), attester, claimType, network);
114
+
115
+ const request = {
116
+ schemaVersion: REQUEST_SCHEMA_VERSION,
117
+ requestType: 'identity-attestation',
118
+ mode: 'unsigned-readonly-request',
119
+ network,
120
+ signingRequired: false,
121
+ unsigned: true,
122
+ subjectWallet,
123
+ agentId,
124
+ attester,
125
+ claimType,
126
+ attestationType: claimType,
127
+ metadataHash,
128
+ proofData: JSON.stringify({ metadataHash }),
129
+ expiresAt,
130
+ agentIdHash,
131
+ genesisPda: genesisPda.toBase58(),
132
+ genesisBump,
133
+ attestationPda: attestationPda.toBase58(),
134
+ attestationBump,
135
+ programs: publicProgramIds(network),
136
+ instructions: [],
137
+ signers: [],
138
+ transaction: null,
139
+ };
140
+
141
+ return {
142
+ ...request,
143
+ requestHash: hashObject(request),
144
+ };
145
+ }
146
+
147
+ module.exports = {
148
+ prepareIdentityAttestationRequest,
149
+ };
@@ -0,0 +1,213 @@
1
+ import { PublicKey } from '@solana/web3.js';
2
+
3
+ /**
4
+ * BorshReader — streaming Borsh deserializer for raw account data.
5
+ */
6
+ export class BorshReader {
7
+ buf: Buffer;
8
+ offset: number;
9
+
10
+ constructor(buf: Buffer, offset?: number);
11
+
12
+ readBytes(n: number): Buffer;
13
+ readU8(): number;
14
+ readU16(): number;
15
+ readU32(): number;
16
+ readU64Num(): number;
17
+ readU64BigInt(): bigint;
18
+ readI64(): number;
19
+ readI64BigInt(): bigint;
20
+ readBool(): boolean;
21
+ readFixedBytes32(): Buffer;
22
+ readPubkey(): PublicKey;
23
+ readPubkeyBase58(): string;
24
+ readString(): string;
25
+ readVecString(): string[];
26
+ readOption<T>(readerFn: (this: BorshReader) => T): T | null;
27
+ readOptionPubkey(): string | null;
28
+ readOptionI64(): number | null;
29
+ readOptionBytes32Hex(): string | null;
30
+ skipDiscriminator(): this;
31
+ remaining(): number;
32
+ }
33
+
34
+ // ─── Parsed Account Types ─────────────────────────
35
+
36
+ export interface ParsedGenesisRecord {
37
+ agentIdHash: string;
38
+ agentName: string;
39
+ description: string;
40
+ category: string;
41
+ capabilities: string[];
42
+ metadataUri: string;
43
+ faceImage: string | null;
44
+ faceMint: string | null;
45
+ faceBurnTx: string | null;
46
+ genesisRecord: number;
47
+ isBorn: boolean;
48
+ isActive: boolean;
49
+ authority: string;
50
+ pendingAuthority: string | null;
51
+ reputationScore: number;
52
+ verificationLevel: number;
53
+ reputationUpdatedAt: number;
54
+ verificationUpdatedAt: number;
55
+ createdAt: number;
56
+ updatedAt: number;
57
+ bump: number;
58
+ }
59
+
60
+ export interface ParsedLinkedWallet {
61
+ identity: string;
62
+ wallet: string;
63
+ chain: string;
64
+ label: string;
65
+ verifiedAt: number;
66
+ isActive: boolean;
67
+ bump: number;
68
+ }
69
+
70
+ export interface ParsedMintTracker {
71
+ identity: string;
72
+ mintCount: number;
73
+ lastMintTimestamp: number;
74
+ bump: number;
75
+ }
76
+
77
+ export interface ParsedNameRegistry {
78
+ name: string;
79
+ nameHash: string;
80
+ identity: string;
81
+ authority: string;
82
+ registeredAt: number;
83
+ isActive: boolean;
84
+ bump: number;
85
+ }
86
+
87
+ export interface ParsedReview {
88
+ agentId: string;
89
+ agentIdHash: string;
90
+ reviewer: string;
91
+ rating: number;
92
+ reviewText: string;
93
+ metadata: string;
94
+ createdAt: number;
95
+ updatedAt: number;
96
+ isActive: boolean;
97
+ bump: number;
98
+ }
99
+
100
+ export interface ParsedReviewCounter {
101
+ agentId: string;
102
+ agentIdHash: string;
103
+ count: number;
104
+ bump: number;
105
+ }
106
+
107
+ export interface ParsedAttestation {
108
+ agentId: string;
109
+ agentIdHash: string;
110
+ attestationType: string;
111
+ issuer: string;
112
+ proofData: string;
113
+ verified: boolean;
114
+ createdAt: number;
115
+ expiresAt: number | null;
116
+ isRevoked: boolean;
117
+ isExpired: boolean;
118
+ isValid: boolean;
119
+ bump: number;
120
+ }
121
+
122
+ export type EscrowStatusV3 =
123
+ | 'Active'
124
+ | 'WorkSubmitted'
125
+ | 'Released'
126
+ | 'Cancelled'
127
+ | 'Disputed'
128
+ | 'Resolved';
129
+
130
+ export interface ParsedEscrowV3 {
131
+ client: string;
132
+ agent: string;
133
+ agentIdHash: string;
134
+ amount: number;
135
+ releasedAmount: number;
136
+ remaining: number;
137
+ descriptionHash: string;
138
+ deadline: number;
139
+ nonce: number;
140
+ status: EscrowStatusV3 | string;
141
+ statusCode: number;
142
+ minVerificationLevel: number;
143
+ requireBorn: boolean;
144
+ createdAt: number;
145
+ arbiter: string;
146
+ workHash: string | null;
147
+ workSubmittedAt: number | null;
148
+ disputeReasonHash: string | null;
149
+ disputedAt: number | null;
150
+ disputedBy: string | null;
151
+ bump: number;
152
+ }
153
+
154
+ // ─── Account Type Names ───────────────────────────
155
+
156
+ export type AccountTypeName =
157
+ | 'GenesisRecord'
158
+ | 'LinkedWallet'
159
+ | 'MintTracker'
160
+ | 'NameRegistry'
161
+ | 'Review'
162
+ | 'ReviewCounter'
163
+ | 'Attestation'
164
+ | 'EscrowV3';
165
+
166
+ export type ParsedAccountData =
167
+ | ParsedGenesisRecord
168
+ | ParsedLinkedWallet
169
+ | ParsedMintTracker
170
+ | ParsedNameRegistry
171
+ | ParsedReview
172
+ | ParsedReviewCounter
173
+ | ParsedAttestation
174
+ | ParsedEscrowV3;
175
+
176
+ // ─── Deserializer Functions ───────────────────────
177
+
178
+ export function deserializeGenesisRecord(data: Buffer): ParsedGenesisRecord;
179
+ export function deserializeLinkedWallet(data: Buffer): ParsedLinkedWallet;
180
+ export function deserializeMintTracker(data: Buffer): ParsedMintTracker;
181
+ export function deserializeNameRegistry(data: Buffer): ParsedNameRegistry;
182
+ export function deserializeReview(data: Buffer): ParsedReview;
183
+ export function deserializeReviewCounter(data: Buffer): ParsedReviewCounter;
184
+ export function deserializeAttestation(data: Buffer): ParsedAttestation;
185
+ export function deserializeEscrowV3(data: Buffer): ParsedEscrowV3;
186
+
187
+ /** Auto-detect and deserialize any SATP V3 account. */
188
+ export function deserializeAccount(data: Buffer): {
189
+ type: AccountTypeName;
190
+ data: ParsedAccountData;
191
+ };
192
+
193
+ /** Batch deserialize getProgramAccounts results. */
194
+ export function deserializeBatch(
195
+ accounts: Array<{ pubkey: PublicKey; account: { data: Buffer } }>,
196
+ expectedType?: AccountTypeName
197
+ ): Array<{
198
+ pubkey: string;
199
+ type: AccountTypeName;
200
+ data: ParsedAccountData;
201
+ }>;
202
+
203
+ /** Get the 8-byte Anchor discriminator for a known account type. */
204
+ export function getAccountDiscriminator(accountName: string): Buffer;
205
+
206
+ /** Compute Anchor account discriminator: SHA256("account:<name>")[0..8] */
207
+ export function accountDiscriminator(accountName: string): Buffer;
208
+
209
+ /** Check if raw data matches a specific account type's discriminator. */
210
+ export function isAccountType(data: Buffer, accountName: AccountTypeName): boolean;
211
+
212
+ /** Pre-computed discriminators for all V3 account types. */
213
+ export const DISCRIMINATORS: Record<AccountTypeName, Buffer>;