@cascade-fyi/sati-sdk 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-02-06
9
+
10
+ ### Added
11
+
12
+ - `updateReputationScore` method for updating existing reputation scores
13
+ - `validateReputationScoreContent` helper function
14
+ - `SAS_DATA_LEN_OFFSET` constant for cleaner SAS account parsing
15
+ - Content validation in `createReputationScore` and `updateReputationScore`
16
+ - Bounds check on content length in `deserializeReputationScore`
17
+ - Known Issues documentation section
18
+
19
+ ### Fixed
20
+
21
+ - **BREAKING**: Migrated reputation scores to ReputationScoreV3 with VecU8 content layout, fixing variable-length JSON content support
22
+ - Fixed SAS credential authorized signers: `satiPda` is now correctly added as an authorized signer, enabling `createReputationScore` to succeed
23
+ - Fixed `fetchMaybeSchema` truthiness check that caused false negatives
24
+ - Fixed `deriveReputationAttestationPda` to use correct nonce format
25
+ - Deploy script is now fully idempotent for authorized signer management
26
+
27
+ ### Changed
28
+
29
+ - `updateReputationScore` defaults to `ContentType.None` (was `ContentType.JSON`)
30
+ - Replaced magic number 97 with named `SAS_DATA_LEN_OFFSET` constant
31
+
8
32
  ## [0.3.0] - 2025-01-27
9
33
 
10
34
  ### Added
@@ -43,5 +67,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
43
67
  - Compressed attestation storage via Light Protocol
44
68
  - Basic querying via Photon RPC
45
69
 
70
+ [0.4.0]: https://github.com/cascade-protocol/sati/compare/v0.3.0...v0.4.0
46
71
  [0.3.0]: https://github.com/cascade-protocol/sati/compare/v0.2.0...v0.3.0
47
72
  [0.2.0]: https://github.com/cascade-protocol/sati/releases/tag/v0.2.0
package/README.md CHANGED
@@ -16,10 +16,10 @@ pnpm add @solana/kit @solana-program/token-2022 @coral-xyz/anchor
16
16
  ## Quick Start
17
17
 
18
18
  ```typescript
19
- import { SATI, Outcome } from "@cascade-fyi/sati-sdk";
19
+ import { Sati, Outcome } from "@cascade-fyi/sati-sdk";
20
20
 
21
21
  // Initialize client
22
- const sati = new SATI({
22
+ const sati = new Sati({
23
23
  network: "devnet",
24
24
  photonRpcUrl: "https://devnet.helius-rpc.com?api-key=YOUR_KEY", // For compressed attestations
25
25
  });
@@ -67,22 +67,15 @@ import {
67
67
  } from "@cascade-fyi/sati-sdk";
68
68
 
69
69
  // 1. Agent signs BEFORE knowing outcome (blind commitment)
70
- const interactionHash = computeInteractionHash({
70
+ const interactionHash = computeInteractionHash(
71
71
  sasSchema,
72
72
  taskRef, // 32-byte task identifier
73
- tokenAccount, // Agent's token address
74
73
  dataHash, // Hash of request data
75
- });
74
+ );
76
75
  const agentSig = await signMessage(agentKeypair, interactionHash);
77
76
 
78
- // 2. After task completion, counterparty signs WITH outcome
79
- const feedbackHash = computeFeedbackHash({
80
- sasSchema,
81
- taskRef,
82
- tokenAccount,
83
- outcome: Outcome.Positive,
84
- });
85
- const counterpartySig = await signMessage(clientKeypair, feedbackHash);
77
+ // 2. After task completion, counterparty signs human-readable SIWS message
78
+ // (built automatically by the SDK)
86
79
  ```
87
80
 
88
81
  ### Create Feedback Attestation
@@ -92,12 +85,10 @@ const result = await sati.createFeedback({
92
85
  payer,
93
86
  sasSchema,
94
87
  taskRef: new Uint8Array(32), // CAIP-220 tx hash or arbitrary ID
95
- tokenAccount: agentMint,
88
+ agentMint, // Agent's NFT mint address
96
89
  counterparty: clientAddress,
97
90
  dataHash: requestHash,
98
91
  outcome: Outcome.Positive, // Negative, Neutral, Positive
99
- tag1: "quality", // Optional, max 32 chars
100
- tag2: "speed", // Optional, max 32 chars
101
92
  content: JSON.stringify({ ... }), // Optional extended data
102
93
  agentSignature: {
103
94
  pubkey: agentAddress,
@@ -121,29 +112,21 @@ Validation attestations are for third-party validators assessing agent work:
121
112
  import { computeValidationHash, ValidationType } from "@cascade-fyi/sati-sdk";
122
113
 
123
114
  // 1. Agent signs blind (same as Feedback)
124
- const interactionHash = computeInteractionHash({
125
- sasSchema: validationSchema,
115
+ const interactionHash = computeInteractionHash(
116
+ validationSchema,
126
117
  taskRef,
127
- tokenAccount: agentMint,
128
- dataHash: workHash, // Hash of work being validated
129
- });
118
+ workHash, // Hash of work being validated
119
+ );
130
120
  const agentSig = await signMessage(agentKeypair, interactionHash);
131
121
 
132
- // 2. Validator signs WITH response score
133
- const validationHash = computeValidationHash({
134
- sasSchema: validationSchema,
135
- taskRef,
136
- tokenAccount: agentMint,
137
- response: 95, // 0-100 score
138
- });
139
- const validatorSig = await signMessage(validatorKeypair, validationHash);
122
+ // 2. Validator signs human-readable SIWS message (built by SDK)
140
123
 
141
124
  // 3. Create attestation
142
125
  const result = await sati.createValidation({
143
126
  payer,
144
127
  sasSchema: validationSchema,
145
128
  taskRef,
146
- tokenAccount: agentMint,
129
+ agentMint,
147
130
  counterparty: validatorAddress,
148
131
  dataHash: workHash,
149
132
  validationType: ValidationType.Automated, // Manual, Automated, Hybrid
@@ -163,31 +146,66 @@ const result = await sati.createValidation({
163
146
  });
164
147
  ```
165
148
 
166
- ### Create ReputationScore (Regular Attestation)
149
+ ### Create ReputationScoreV3 (Regular Attestation)
150
+
151
+ ReputationScoreV3 uses VecU8 for variable-length content and CounterpartySigned mode (provider only).
167
152
 
168
153
  ```typescript
169
- import { computeReputationHash } from "@cascade-fyi/sati-sdk";
154
+ import {
155
+ computeInteractionHash,
156
+ computeReputationNonce,
157
+ zeroDataHash,
158
+ createJsonContent,
159
+ ContentType,
160
+ } from "@cascade-fyi/sati-sdk";
170
161
 
171
- // Provider signs the score
172
- const reputationHash = computeReputationHash({
173
- sasSchema,
174
- tokenAccount: agentMint,
162
+ // Compute deterministic nonce and taskRef
163
+ const nonce = computeReputationNonce(providerAddress, agentMint);
164
+ const taskRef = nonce; // same as nonce per spec
165
+ const dataHash = zeroDataHash();
166
+
167
+ // Provider signs the interaction hash
168
+ const interactionHash = computeInteractionHash(sasSchema, taskRef, dataHash);
169
+ const providerSig = await signMessage(providerKeypair, interactionHash);
170
+
171
+ const result = await sati.createReputationScore({
172
+ payer,
175
173
  provider: providerAddress,
176
- score: 85,
174
+ providerSignature: providerSig,
175
+ sasSchema,
176
+ satiCredential,
177
+ agentMint,
178
+ taskRef,
179
+ dataHash,
180
+ outcome: Outcome.Positive,
181
+ contentType: ContentType.JSON,
182
+ content: createJsonContent({
183
+ score: 85,
184
+ methodology: "weighted_feedback",
185
+ feedbackCount: 127,
186
+ validationCount: 5,
187
+ }),
177
188
  });
178
- const providerSig = await signMessage(providerKeypair, reputationHash);
189
+ ```
179
190
 
180
- const result = await sati.createReputationScore({
191
+ ### Update ReputationScoreV3
192
+
193
+ High-level convenience method that closes the existing score and creates a new one:
194
+
195
+ ```typescript
196
+ const result = await sati.updateReputationScore({
181
197
  payer,
198
+ provider: providerKeypair, // Must be KeyPairSigner (signs close + create)
182
199
  sasSchema,
183
200
  satiCredential,
184
- tokenAccount: agentMint,
185
- provider: providerAddress,
186
- score: 85, // 0-100 normalized score
187
- providerSignature: providerSig,
188
- content: JSON.stringify({
201
+ agentMint,
202
+ outcome: Outcome.Positive,
203
+ contentType: ContentType.JSON,
204
+ content: createJsonContent({
205
+ score: 90,
189
206
  methodology: "weighted_feedback",
190
- feedback_count: 127,
207
+ feedbackCount: 150,
208
+ validationCount: 8,
191
209
  }),
192
210
  });
193
211
  ```
@@ -206,7 +224,7 @@ const attestations = await rpc.getCompressedAccountsByOwner({
206
224
  owner: SATI_PROGRAM_ADDRESS,
207
225
  filters: [
208
226
  { memcmp: { offset: 8, bytes: feedbackSchema } },
209
- { memcmp: { offset: 40, bytes: agentTokenAccount } },
227
+ { memcmp: { offset: 40, bytes: agentMint } },
210
228
  ],
211
229
  });
212
230
 
@@ -216,7 +234,7 @@ const toClose = attestations.value.items[0];
216
234
  const result = await sati.closeAttestation({
217
235
  payer,
218
236
  sasSchema: feedbackSchema,
219
- tokenAccount: agentTokenAccount,
237
+ agentMint,
220
238
  // Current attestation state (for proof verification)
221
239
  dataType: DataType.Feedback,
222
240
  currentData: toClose.data,
@@ -229,20 +247,20 @@ const result = await sati.closeAttestation({
229
247
  console.log(result.signature); // Transaction signature
230
248
  ```
231
249
 
232
- ### Close Regular Attestation (ReputationScore)
250
+ ### Close Regular Attestation (ReputationScoreV3)
233
251
 
234
252
  ```typescript
235
- // ReputationScore uses SAS storage, close via PDA
253
+ // ReputationScoreV3 uses SAS storage, close via PDA
236
254
  const result = await sati.closeReputationScore({
237
255
  payer,
238
256
  sasSchema: reputationSchema,
239
257
  satiCredential,
240
- tokenAccount: agentMint,
258
+ agentMint,
241
259
  provider: providerAddress, // Provider who created it
242
260
  });
243
261
  ```
244
262
 
245
- **Note:** Only the original provider can close a ReputationScore. Compressed attestations can be closed by anyone with the proof, but the rent goes back to the original payer.
263
+ **Note:** Only the original provider can close a ReputationScoreV3. Compressed attestations can be closed by anyone with the proof, but the rent goes back to the original payer.
246
264
 
247
265
  ---
248
266
 
@@ -262,8 +280,8 @@ const feedbacks = await rpc.getCompressedAccountsByOwner({
262
280
  filters: [
263
281
  // sas_schema at offset 8 (after 8-byte Light discriminator)
264
282
  { memcmp: { offset: 8, bytes: feedbackSchemaAddress } },
265
- // token_account at offset 40
266
- { memcmp: { offset: 40, bytes: agentTokenAccount } },
283
+ // agent_mint at offset 40
284
+ { memcmp: { offset: 40, bytes: agentMint } },
267
285
  ],
268
286
  limit: 50,
269
287
  });
@@ -282,7 +300,7 @@ The SDK exports offset constants for filtering:
282
300
 
283
301
  ```typescript
284
302
  import {
285
- COMPRESSED_OFFSETS, // Base offsets (sas_schema, token_account)
303
+ COMPRESSED_OFFSETS, // Base offsets (sas_schema, agent_mint)
286
304
  FEEDBACK_OFFSETS, // Feedback-specific (outcome at 129 + 8)
287
305
  VALIDATION_OFFSETS, // Validation-specific (response at 130 + 8)
288
306
  } from "@cascade-fyi/sati-sdk";
@@ -291,7 +309,7 @@ import {
291
309
  | Field | Offset | Notes |
292
310
  |-------|--------|-------|
293
311
  | `sas_schema` | 8 | Filter by attestation type |
294
- | `token_account` | 40 | Filter by agent |
312
+ | `agent_mint` | 40 | Filter by agent |
295
313
  | `outcome` (Feedback) | 137 | 0=Negative, 1=Neutral, 2=Positive |
296
314
  | `response` (Validation) | 138 | Score 0-100 |
297
315
 
@@ -345,7 +363,7 @@ const result = await sati.createFeedback({
345
363
  payer,
346
364
  sasSchema,
347
365
  taskRef,
348
- tokenAccount: agentMint,
366
+ agentMint,
349
367
  counterparty: clientAddress,
350
368
  dataHash: requestHash,
351
369
  outcome: Outcome.Positive,
@@ -545,7 +563,7 @@ try {
545
563
  |-------|-------|----------|
546
564
  | `InvalidSignatureCount` | Wrong number of sigs for SignatureMode | DualSignature needs 2, SingleSigner needs 1 |
547
565
  | `SignatureMismatch` | Sig pubkey doesn't match expected | Verify agent signs interaction hash, counterparty signs feedback hash |
548
- | `SelfAttestationNotAllowed` | `tokenAccount == counterparty` | Use different addresses for agent and counterparty |
566
+ | `SelfAttestationNotAllowed` | `agentMint == counterparty` | Use different addresses for agent and counterparty |
549
567
  | `InvalidAuthority` | Signer is not registry authority | Only authority can register schemas |
550
568
  | `ImmutableAuthority` | Registry authority was renounced | Cannot modify immutable registry |
551
569
  | `AttestationNotCloseable` | Schema has `closeable: false` | Use a different schema or don't close |
@@ -602,7 +620,7 @@ function validateFeedbackParams(params: CreateFeedbackParams) {
602
620
  if (params.content && params.content.length > MAX_CONTENT_SIZE) {
603
621
  throw new Error(`content exceeds ${MAX_CONTENT_SIZE} bytes`);
604
622
  }
605
- if (params.tokenAccount === params.counterparty) {
623
+ if (params.agentMint === params.counterparty) {
606
624
  throw new Error("Self-attestation not allowed");
607
625
  }
608
626
  }
@@ -616,10 +634,12 @@ function validateFeedbackParams(params: CreateFeedbackParams) {
616
634
 
617
635
  ```typescript
618
636
  import {
619
- computeInteractionHash, // Agent signs (blind to outcome)
620
- computeFeedbackHash, // Counterparty signs (with outcome)
621
- computeValidationHash, // Validator signs (with response)
622
- computeReputationHash, // Provider signs (with score)
637
+ computeInteractionHash, // Agent signs (blind to outcome)
638
+ computeAttestationNonce, // Deterministic nonce for compressed attestation address
639
+ computeReputationNonce, // Deterministic nonce for ReputationScoreV3 (one per provider+agent)
640
+ computeDataHash, // Hash request + response content
641
+ computeDataHashFromStrings, // Convenience wrapper for string content
642
+ zeroDataHash, // Zero-filled hash for CounterpartySigned schemas
623
643
  } from "@cascade-fyi/sati-sdk";
624
644
  ```
625
645