@brainai/satp-client 2.0.0 → 2.0.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/README.md +435 -51
- package/package.json +24 -4
- 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 +266 -0
- package/src/index.js +642 -0
- package/src/pda.js +43 -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/pda.js
CHANGED
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
REVIEWS_AUTHORITY_SEED,
|
|
10
10
|
ATTESTATION_SEED,
|
|
11
11
|
REVIEW_SEED,
|
|
12
|
+
ESCROW_SEED,
|
|
12
13
|
} = require('./constants');
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -136,6 +137,46 @@ function getReviewAttestationPDA(agentId, reviewsAuthority, reviewer, network =
|
|
|
136
137
|
);
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Derive the Escrow PDA for a client + description_hash pair.
|
|
142
|
+
* Seeds: ["escrow", client_pubkey, description_hash]
|
|
143
|
+
* @param {PublicKey|string} client - Client wallet
|
|
144
|
+
* @param {Buffer|number[]} descriptionHash - 32-byte SHA256 hash of job description
|
|
145
|
+
* @param {'mainnet'|'devnet'} network
|
|
146
|
+
*/
|
|
147
|
+
function getEscrowPDA(client, descriptionHash, network = 'devnet') {
|
|
148
|
+
const clientKey = new PublicKey(client);
|
|
149
|
+
const hashBuf = Buffer.isBuffer(descriptionHash)
|
|
150
|
+
? descriptionHash
|
|
151
|
+
: Buffer.from(descriptionHash);
|
|
152
|
+
const programIds = getProgramIds(network);
|
|
153
|
+
if (!programIds.ESCROW) {
|
|
154
|
+
throw new Error('SATP v2 mainnet escrow program ID is not configured; pass devnet or wait for an approved mainnet escrow deployment');
|
|
155
|
+
}
|
|
156
|
+
return PublicKey.findProgramAddressSync(
|
|
157
|
+
[Buffer.from(ESCROW_SEED), clientKey.toBuffer(), hashBuf],
|
|
158
|
+
programIds.ESCROW
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Derive the Review V3 PDA (job-scoped reviews).
|
|
164
|
+
* Seeds: ["review", job_pubkey, reviewer_pubkey]
|
|
165
|
+
* @param {PublicKey|string} jobPDA - Job/Escrow account pubkey
|
|
166
|
+
* @param {PublicKey|string} reviewer - Reviewer wallet
|
|
167
|
+
* @param {'mainnet'|'devnet'} network
|
|
168
|
+
*/
|
|
169
|
+
function getReviewV3PDA(jobPDA, reviewer, network = 'devnet') {
|
|
170
|
+
const jobKey = new PublicKey(jobPDA);
|
|
171
|
+
const reviewerKey = new PublicKey(reviewer);
|
|
172
|
+
const programIds = getProgramIds(network);
|
|
173
|
+
// Reviews V3 uses the same REVIEWS program ID
|
|
174
|
+
return PublicKey.findProgramAddressSync(
|
|
175
|
+
[Buffer.from(REVIEW_SEED), jobKey.toBuffer(), reviewerKey.toBuffer()],
|
|
176
|
+
programIds.REVIEWS
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
139
180
|
module.exports = {
|
|
140
181
|
getIdentityPDA,
|
|
141
182
|
getReputationAuthorityPDA,
|
|
@@ -145,4 +186,6 @@ module.exports = {
|
|
|
145
186
|
getReviewsAuthorityPDA,
|
|
146
187
|
getReviewPDA,
|
|
147
188
|
getReviewAttestationPDA,
|
|
189
|
+
getEscrowPDA,
|
|
190
|
+
getReviewV3PDA,
|
|
148
191
|
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { prepareIdentityAttestationRequest } = require('./attestation-request');
|
|
4
|
+
|
|
5
|
+
const TRUST_PACKET_SCHEMA_VERSION = 'satp.trustPacket.v1';
|
|
6
|
+
|
|
7
|
+
function canonicalStringify(value) {
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
return '[' + value.map(canonicalStringify).join(',') + ']';
|
|
10
|
+
}
|
|
11
|
+
if (value && typeof value === 'object') {
|
|
12
|
+
return '{' + Object.keys(value).sort().map((key) => JSON.stringify(key) + ':' + canonicalStringify(value[key])).join(',') + '}';
|
|
13
|
+
}
|
|
14
|
+
return JSON.stringify(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function sameJsonValue(left, right) {
|
|
18
|
+
return canonicalStringify(left) === canonicalStringify(right);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build a deterministic, read-only SATP trust packet for consumer preflight.
|
|
23
|
+
*
|
|
24
|
+
* The packet wraps the unsigned identity-attestation request with the derived
|
|
25
|
+
* program IDs, Genesis PDA, attestation PDA, request hash, and explicit flags
|
|
26
|
+
* proving that no signer, transaction, instruction, RPC write, or payment is
|
|
27
|
+
* required to consume it.
|
|
28
|
+
*/
|
|
29
|
+
function buildSatpTrustPacket(opts = {}) {
|
|
30
|
+
const request = prepareIdentityAttestationRequest(opts);
|
|
31
|
+
return {
|
|
32
|
+
schemaVersion: TRUST_PACKET_SCHEMA_VERSION,
|
|
33
|
+
packetType: 'satp-trust-packet',
|
|
34
|
+
mode: 'offline-readonly-trust-packet',
|
|
35
|
+
network: request.network,
|
|
36
|
+
subjectWallet: request.subjectWallet,
|
|
37
|
+
agentId: request.agentId,
|
|
38
|
+
claimType: request.claimType,
|
|
39
|
+
attestationType: request.attestationType,
|
|
40
|
+
metadataHash: request.metadataHash,
|
|
41
|
+
attester: request.attester,
|
|
42
|
+
expiresAt: request.expiresAt,
|
|
43
|
+
programs: { ...request.programs },
|
|
44
|
+
pda: {
|
|
45
|
+
genesis: request.genesisPda,
|
|
46
|
+
genesisBump: request.genesisBump,
|
|
47
|
+
attestation: request.attestationPda,
|
|
48
|
+
attestationBump: request.attestationBump,
|
|
49
|
+
},
|
|
50
|
+
requestHash: request.requestHash,
|
|
51
|
+
flags: {
|
|
52
|
+
signingRequired: false,
|
|
53
|
+
transactionRequired: false,
|
|
54
|
+
writesRequired: false,
|
|
55
|
+
livePaymentRequired: false,
|
|
56
|
+
unsigned: true,
|
|
57
|
+
noSign: true,
|
|
58
|
+
noTransaction: true,
|
|
59
|
+
},
|
|
60
|
+
instructions: [],
|
|
61
|
+
signers: [],
|
|
62
|
+
transaction: null,
|
|
63
|
+
request,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function validateSatpTrustPacket(packet) {
|
|
68
|
+
const errors = [];
|
|
69
|
+
if (!packet || typeof packet !== 'object') {
|
|
70
|
+
return { ok: false, errors: ['packet must be an object'] };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (packet.schemaVersion !== TRUST_PACKET_SCHEMA_VERSION) {
|
|
74
|
+
errors.push('schemaVersion must be ' + TRUST_PACKET_SCHEMA_VERSION);
|
|
75
|
+
}
|
|
76
|
+
if (packet.packetType !== 'satp-trust-packet') {
|
|
77
|
+
errors.push('packetType must be satp-trust-packet');
|
|
78
|
+
}
|
|
79
|
+
if (packet.mode !== 'offline-readonly-trust-packet') {
|
|
80
|
+
errors.push('mode must be offline-readonly-trust-packet');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const expectedFlags = {
|
|
84
|
+
signingRequired: false,
|
|
85
|
+
transactionRequired: false,
|
|
86
|
+
writesRequired: false,
|
|
87
|
+
livePaymentRequired: false,
|
|
88
|
+
unsigned: true,
|
|
89
|
+
noSign: true,
|
|
90
|
+
noTransaction: true,
|
|
91
|
+
};
|
|
92
|
+
if (!sameJsonValue(packet.flags, expectedFlags)) {
|
|
93
|
+
errors.push('flags must be read-only, unsigned, and no-transaction');
|
|
94
|
+
}
|
|
95
|
+
if (!Array.isArray(packet.instructions) || packet.instructions.length !== 0) {
|
|
96
|
+
errors.push('instructions must be an empty array');
|
|
97
|
+
}
|
|
98
|
+
if (!Array.isArray(packet.signers) || packet.signers.length !== 0) {
|
|
99
|
+
errors.push('signers must be an empty array');
|
|
100
|
+
}
|
|
101
|
+
if (packet.transaction !== null) {
|
|
102
|
+
errors.push('transaction must be null');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let expected;
|
|
106
|
+
try {
|
|
107
|
+
expected = buildSatpTrustPacket({
|
|
108
|
+
subjectWallet: packet.subjectWallet,
|
|
109
|
+
agentId: packet.agentId,
|
|
110
|
+
claimType: packet.claimType || packet.attestationType,
|
|
111
|
+
metadataHash: packet.metadataHash,
|
|
112
|
+
attester: packet.attester,
|
|
113
|
+
network: packet.network,
|
|
114
|
+
expiresAt: packet.expiresAt,
|
|
115
|
+
});
|
|
116
|
+
} catch (err) {
|
|
117
|
+
errors.push('packet cannot be re-derived: ' + err.message);
|
|
118
|
+
return { ok: false, errors };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const fields = [
|
|
122
|
+
'network',
|
|
123
|
+
'subjectWallet',
|
|
124
|
+
'agentId',
|
|
125
|
+
'claimType',
|
|
126
|
+
'attestationType',
|
|
127
|
+
'metadataHash',
|
|
128
|
+
'attester',
|
|
129
|
+
'expiresAt',
|
|
130
|
+
'programs',
|
|
131
|
+
'pda',
|
|
132
|
+
'requestHash',
|
|
133
|
+
'request',
|
|
134
|
+
];
|
|
135
|
+
for (const field of fields) {
|
|
136
|
+
if (!sameJsonValue(packet[field], expected[field])) {
|
|
137
|
+
errors.push(field + ' does not match derived trust packet');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return { ok: errors.length === 0, errors };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
TRUST_PACKET_SCHEMA_VERSION,
|
|
146
|
+
buildSatpTrustPacket,
|
|
147
|
+
validateSatpTrustPacket,
|
|
148
|
+
};
|
package/src/v3-pda.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { PublicKey } from '@solana/web3.js';
|
|
2
|
+
|
|
3
|
+
export type Network = 'mainnet' | 'devnet';
|
|
4
|
+
|
|
5
|
+
export interface V3ProgramIds {
|
|
6
|
+
IDENTITY: PublicKey;
|
|
7
|
+
REVIEWS: PublicKey;
|
|
8
|
+
REPUTATION: PublicKey;
|
|
9
|
+
ATTESTATIONS: PublicKey;
|
|
10
|
+
VALIDATION: PublicKey;
|
|
11
|
+
ESCROW: PublicKey;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Get all V3 program IDs for a network. */
|
|
15
|
+
export function getV3ProgramIds(network?: Network): V3ProgramIds;
|
|
16
|
+
|
|
17
|
+
/** SHA-256 hash of agent ID string. Returns 32-byte Buffer. */
|
|
18
|
+
export function hashAgentId(agentId: string): Buffer;
|
|
19
|
+
|
|
20
|
+
/** SHA-256 hash of lowercased name string. Returns 32-byte Buffer. */
|
|
21
|
+
export function hashName(name: string): Buffer;
|
|
22
|
+
|
|
23
|
+
/** Derive Genesis PDA from agent ID hash. */
|
|
24
|
+
export function getGenesisPDA(agentIdHash: string | Buffer, network?: Network): [PublicKey, number];
|
|
25
|
+
|
|
26
|
+
/** Derive Reputation V3 Authority PDA. */
|
|
27
|
+
export function getV3ReputationAuthorityPDA(network?: Network): [PublicKey, number];
|
|
28
|
+
|
|
29
|
+
/** Derive Validation V3 Authority PDA. */
|
|
30
|
+
export function getV3ValidationAuthorityPDA(network?: Network): [PublicKey, number];
|
|
31
|
+
|
|
32
|
+
/** Derive MintTracker PDA from Genesis PDA. */
|
|
33
|
+
export function getV3MintTrackerPDA(genesisPDA: PublicKey | string, network?: Network): [PublicKey, number];
|
|
34
|
+
|
|
35
|
+
/** Derive NameRegistry PDA from name hash. */
|
|
36
|
+
export function getNameRegistryPDA(nameHash: string | Buffer, network?: Network): [PublicKey, number];
|
|
37
|
+
|
|
38
|
+
/** Derive LinkedWallet PDA from Genesis PDA and wallet. */
|
|
39
|
+
export function getLinkedWalletPDA(
|
|
40
|
+
genesisPDA: PublicKey | string,
|
|
41
|
+
wallet: PublicKey | string,
|
|
42
|
+
network?: Network
|
|
43
|
+
): [PublicKey, number];
|
|
44
|
+
|
|
45
|
+
/** Derive Review V3 PDA from job account and reviewer. */
|
|
46
|
+
export function getV3ReviewPDA(
|
|
47
|
+
jobPDA: PublicKey | string,
|
|
48
|
+
reviewer: PublicKey | string,
|
|
49
|
+
network?: Network
|
|
50
|
+
): [PublicKey, number];
|
|
51
|
+
|
|
52
|
+
/** Derive Attestation PDA from agent ID hash, attester, and type. */
|
|
53
|
+
export function getV3AttestationPDA(
|
|
54
|
+
agentIdHash: string | Buffer,
|
|
55
|
+
attester: PublicKey | string,
|
|
56
|
+
attestationType: string,
|
|
57
|
+
network?: Network
|
|
58
|
+
): [PublicKey, number];
|
|
59
|
+
|
|
60
|
+
/** Derive Review Counter PDA from agent ID. */
|
|
61
|
+
export function getV3ReviewCounterPDA(
|
|
62
|
+
agentIdOrHash: string | Buffer,
|
|
63
|
+
network?: Network
|
|
64
|
+
): [PublicKey, number];
|
|
65
|
+
|
|
66
|
+
/** Derive Escrow V3 PDA from client, description hash, and nonce. */
|
|
67
|
+
export function getV3EscrowPDA(
|
|
68
|
+
client: PublicKey | string,
|
|
69
|
+
descriptionHash: Buffer,
|
|
70
|
+
nonce: number | bigint,
|
|
71
|
+
network?: Network
|
|
72
|
+
): [PublicKey, number];
|
package/src/v3-pda.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
const { PublicKey } = require('@solana/web3.js');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
// ═══════════════════════════════════════════════════
|
|
5
|
+
// SATP V3 Program IDs — Devnet
|
|
6
|
+
// ═══════════════════════════════════════════════════
|
|
7
|
+
|
|
8
|
+
const V3_DEVNET_PROGRAM_IDS = {
|
|
9
|
+
IDENTITY: new PublicKey('GTppU4E44BqXTQgbqMZ68ozFzhP1TLty3EGnzzjtNZfG'),
|
|
10
|
+
REVIEWS: new PublicKey('r9XX4frcqxxAZ6Au9V5PA3EAxs1zoNckqLLmoSRcNr4'),
|
|
11
|
+
REPUTATION: new PublicKey('2Lz7KzMvKdrGeAuS8WPHu7jK2yScrnKVgacpYVEuDjkJ'),
|
|
12
|
+
ATTESTATIONS: new PublicKey('6Xd1dAQJPvQRJ4Ntr6LtPTjDjPUZ8nfnmYLZaZ2DtrdD'),
|
|
13
|
+
VALIDATION: new PublicKey('6rYRiCYidJYV7QvKrzKGgNu4oMh6BAvynked69R7xMbV'),
|
|
14
|
+
ESCROW: new PublicKey('HXCUWKR2NvRcZ7rNAJHwPcH6QAAWaLR4bRFbfyuDND6C'),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const V3_MAINNET_PROGRAM_IDS = null;
|
|
18
|
+
|
|
19
|
+
// ═══════════════════════════════════════════════════
|
|
20
|
+
// V3 PDA Seeds — must match on-chain programs
|
|
21
|
+
// ═══════════════════════════════════════════════════
|
|
22
|
+
|
|
23
|
+
const V3_SEEDS = {
|
|
24
|
+
GENESIS: 'genesis',
|
|
25
|
+
REPUTATION_AUTHORITY: 'reputation_v3_authority',
|
|
26
|
+
VALIDATION_AUTHORITY: 'validation_v3_authority',
|
|
27
|
+
MINT_TRACKER: 'mint_tracker',
|
|
28
|
+
NAME_REGISTRY: 'name_registry',
|
|
29
|
+
LINKED_WALLET: 'linked_wallet',
|
|
30
|
+
REVIEW: 'review_v3',
|
|
31
|
+
REVIEW_COUNTER: 'review_counter_v3',
|
|
32
|
+
ATTESTATION: 'attestation_v3',
|
|
33
|
+
ESCROW_V3: 'escrow_v3',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ═══════════════════════════════════════════════════
|
|
37
|
+
// V3 Program ID Getter
|
|
38
|
+
// ═══════════════════════════════════════════════════
|
|
39
|
+
|
|
40
|
+
function getV3ProgramIds(network = 'devnet') {
|
|
41
|
+
if (network !== 'devnet' && network !== 'mainnet') {
|
|
42
|
+
throw new Error('Invalid network: expected devnet or mainnet');
|
|
43
|
+
}
|
|
44
|
+
if (network === 'mainnet') {
|
|
45
|
+
throw new Error('SATP V3 mainnet program IDs are not configured; use devnet or provide an approved mainnet decision packet before enabling mainnet');
|
|
46
|
+
}
|
|
47
|
+
return V3_DEVNET_PROGRAM_IDS;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ═══════════════════════════════════════════════════
|
|
51
|
+
// Utility: Hash agent_id to 32-byte seed
|
|
52
|
+
// ═══════════════════════════════════════════════════
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Hash an agent_id string to 32-byte SHA-256 (matches on-chain solana_program::hash::hash).
|
|
56
|
+
* @param {string} agentId - Agent identifier string
|
|
57
|
+
* @returns {Buffer} 32-byte hash
|
|
58
|
+
*/
|
|
59
|
+
function hashAgentId(agentId) {
|
|
60
|
+
return crypto.createHash('sha256').update(agentId, 'utf8').digest();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Hash a name (lowercased) for name registry PDA.
|
|
65
|
+
* @param {string} name
|
|
66
|
+
* @returns {Buffer} 32-byte SHA-256 hash
|
|
67
|
+
*/
|
|
68
|
+
function hashName(name) {
|
|
69
|
+
return crypto.createHash('sha256').update(name.toLowerCase(), 'utf8').digest();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ═══════════════════════════════════════════════════
|
|
73
|
+
// V3 PDA Derivation Functions
|
|
74
|
+
// ═══════════════════════════════════════════════════
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Derive Genesis Record PDA.
|
|
78
|
+
* Seeds: ["genesis", agent_id_hash]
|
|
79
|
+
* @param {string|Buffer} agentIdOrHash - agent_id string or 32-byte hash
|
|
80
|
+
* @param {'mainnet'|'devnet'} network
|
|
81
|
+
* @returns {[PublicKey, number]} [pda, bump]
|
|
82
|
+
*/
|
|
83
|
+
function getGenesisPDA(agentIdOrHash, network = 'devnet') {
|
|
84
|
+
const hash = typeof agentIdOrHash === 'string'
|
|
85
|
+
? hashAgentId(agentIdOrHash)
|
|
86
|
+
: Buffer.from(agentIdOrHash);
|
|
87
|
+
const programIds = getV3ProgramIds(network);
|
|
88
|
+
return PublicKey.findProgramAddressSync(
|
|
89
|
+
[Buffer.from(V3_SEEDS.GENESIS), hash],
|
|
90
|
+
programIds.IDENTITY
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Derive Reputation V3 Authority PDA (CPI signer).
|
|
96
|
+
* Seeds: ["reputation_v3_authority"]
|
|
97
|
+
* @param {'mainnet'|'devnet'} network
|
|
98
|
+
* @returns {[PublicKey, number]}
|
|
99
|
+
*/
|
|
100
|
+
function getV3ReputationAuthorityPDA(network = 'devnet') {
|
|
101
|
+
const programIds = getV3ProgramIds(network);
|
|
102
|
+
return PublicKey.findProgramAddressSync(
|
|
103
|
+
[Buffer.from(V3_SEEDS.REPUTATION_AUTHORITY)],
|
|
104
|
+
programIds.REPUTATION
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Derive Validation V3 Authority PDA (CPI signer).
|
|
110
|
+
* Seeds: ["validation_v3_authority"]
|
|
111
|
+
* @param {'mainnet'|'devnet'} network
|
|
112
|
+
* @returns {[PublicKey, number]}
|
|
113
|
+
*/
|
|
114
|
+
function getV3ValidationAuthorityPDA(network = 'devnet') {
|
|
115
|
+
const programIds = getV3ProgramIds(network);
|
|
116
|
+
return PublicKey.findProgramAddressSync(
|
|
117
|
+
[Buffer.from(V3_SEEDS.VALIDATION_AUTHORITY)],
|
|
118
|
+
programIds.VALIDATION
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Derive MintTracker PDA.
|
|
124
|
+
* Seeds: ["mint_tracker", genesis_pda]
|
|
125
|
+
* @param {PublicKey|string} genesisPDA
|
|
126
|
+
* @param {'mainnet'|'devnet'} network
|
|
127
|
+
* @returns {[PublicKey, number]}
|
|
128
|
+
*/
|
|
129
|
+
function getV3MintTrackerPDA(genesisPDA, network = 'devnet') {
|
|
130
|
+
const genesisKey = new PublicKey(genesisPDA);
|
|
131
|
+
const programIds = getV3ProgramIds(network);
|
|
132
|
+
return PublicKey.findProgramAddressSync(
|
|
133
|
+
[Buffer.from(V3_SEEDS.MINT_TRACKER), genesisKey.toBuffer()],
|
|
134
|
+
programIds.IDENTITY
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Derive Name Registry PDA.
|
|
140
|
+
* Seeds: ["name_registry", name_hash]
|
|
141
|
+
* @param {string|Buffer} nameOrHash - Display name string or 32-byte hash
|
|
142
|
+
* @param {'mainnet'|'devnet'} network
|
|
143
|
+
* @returns {[PublicKey, number]}
|
|
144
|
+
*/
|
|
145
|
+
function getNameRegistryPDA(nameOrHash, network = 'devnet') {
|
|
146
|
+
const hash = typeof nameOrHash === 'string'
|
|
147
|
+
? hashName(nameOrHash)
|
|
148
|
+
: Buffer.from(nameOrHash);
|
|
149
|
+
const programIds = getV3ProgramIds(network);
|
|
150
|
+
return PublicKey.findProgramAddressSync(
|
|
151
|
+
[Buffer.from(V3_SEEDS.NAME_REGISTRY), hash],
|
|
152
|
+
programIds.IDENTITY
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Derive Linked Wallet PDA.
|
|
158
|
+
* Seeds: ["linked_wallet", genesis_pda, wallet]
|
|
159
|
+
* @param {PublicKey|string} genesisPDA
|
|
160
|
+
* @param {PublicKey|string} wallet
|
|
161
|
+
* @param {'mainnet'|'devnet'} network
|
|
162
|
+
* @returns {[PublicKey, number]}
|
|
163
|
+
*/
|
|
164
|
+
function getLinkedWalletPDA(genesisPDA, wallet, network = 'devnet') {
|
|
165
|
+
const genesisKey = new PublicKey(genesisPDA);
|
|
166
|
+
const walletKey = new PublicKey(wallet);
|
|
167
|
+
const programIds = getV3ProgramIds(network);
|
|
168
|
+
return PublicKey.findProgramAddressSync(
|
|
169
|
+
[Buffer.from(V3_SEEDS.LINKED_WALLET), genesisKey.toBuffer(), walletKey.toBuffer()],
|
|
170
|
+
programIds.IDENTITY
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Derive V3 Review PDA (agent-scoped).
|
|
176
|
+
* Seeds: ["review_v3", SHA256(agent_id), reviewer]
|
|
177
|
+
* @param {string|Buffer} agentIdOrHash - agent_id string or 32-byte hash
|
|
178
|
+
* @param {PublicKey|string} reviewer
|
|
179
|
+
* @param {'mainnet'|'devnet'} network
|
|
180
|
+
* @returns {[PublicKey, number]}
|
|
181
|
+
*/
|
|
182
|
+
function getV3ReviewPDA(agentIdOrHash, reviewer, network = 'devnet') {
|
|
183
|
+
const hash = typeof agentIdOrHash === 'string'
|
|
184
|
+
? hashAgentId(agentIdOrHash)
|
|
185
|
+
: Buffer.from(agentIdOrHash);
|
|
186
|
+
const reviewerKey = new PublicKey(reviewer);
|
|
187
|
+
const programIds = getV3ProgramIds(network);
|
|
188
|
+
return PublicKey.findProgramAddressSync(
|
|
189
|
+
[Buffer.from(V3_SEEDS.REVIEW), hash, reviewerKey.toBuffer()],
|
|
190
|
+
programIds.REVIEWS
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Derive V3 Review Counter PDA.
|
|
196
|
+
* Seeds: ["review_counter_v3", SHA256(agent_id)]
|
|
197
|
+
* @param {string|Buffer} agentIdOrHash - agent_id string or 32-byte hash
|
|
198
|
+
* @param {'mainnet'|'devnet'} network
|
|
199
|
+
* @returns {[PublicKey, number]}
|
|
200
|
+
*/
|
|
201
|
+
function getV3ReviewCounterPDA(agentIdOrHash, network = 'devnet') {
|
|
202
|
+
const hash = typeof agentIdOrHash === 'string'
|
|
203
|
+
? hashAgentId(agentIdOrHash)
|
|
204
|
+
: Buffer.from(agentIdOrHash);
|
|
205
|
+
const programIds = getV3ProgramIds(network);
|
|
206
|
+
return PublicKey.findProgramAddressSync(
|
|
207
|
+
[Buffer.from(V3_SEEDS.REVIEW_COUNTER), hash],
|
|
208
|
+
programIds.REVIEWS
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Derive V3 Attestation PDA.
|
|
214
|
+
* Seeds: ["attestation", agent_id_hash, attester, attestation_type]
|
|
215
|
+
* @param {string|Buffer} agentIdOrHash
|
|
216
|
+
* @param {PublicKey|string} attester
|
|
217
|
+
* @param {string} attestationType
|
|
218
|
+
* @param {'mainnet'|'devnet'} network
|
|
219
|
+
* @returns {[PublicKey, number]}
|
|
220
|
+
*/
|
|
221
|
+
function getV3AttestationPDA(agentIdOrHash, attester, attestationType, network = 'devnet') {
|
|
222
|
+
const hash = typeof agentIdOrHash === 'string'
|
|
223
|
+
? hashAgentId(agentIdOrHash)
|
|
224
|
+
: Buffer.from(agentIdOrHash);
|
|
225
|
+
const attesterKey = new PublicKey(attester);
|
|
226
|
+
const programIds = getV3ProgramIds(network);
|
|
227
|
+
return PublicKey.findProgramAddressSync(
|
|
228
|
+
[
|
|
229
|
+
Buffer.from(V3_SEEDS.ATTESTATION),
|
|
230
|
+
hash,
|
|
231
|
+
attesterKey.toBuffer(),
|
|
232
|
+
Buffer.from(attestationType, 'utf8'),
|
|
233
|
+
],
|
|
234
|
+
programIds.ATTESTATIONS
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Derive Escrow V3 PDA.
|
|
240
|
+
* Seeds: ["escrow_v3", client, description_hash, nonce_le_bytes]
|
|
241
|
+
* @param {PublicKey|string} client - Client wallet
|
|
242
|
+
* @param {Buffer} descriptionHash - 32-byte SHA-256 of job description
|
|
243
|
+
* @param {number|bigint} nonce - Unique nonce (u64)
|
|
244
|
+
* @param {'mainnet'|'devnet'} network
|
|
245
|
+
* @returns {[PublicKey, number]}
|
|
246
|
+
*/
|
|
247
|
+
function getV3EscrowPDA(client, descriptionHash, nonce, network = 'devnet') {
|
|
248
|
+
const clientKey = new PublicKey(client);
|
|
249
|
+
const hashBuf = Buffer.isBuffer(descriptionHash) ? descriptionHash : Buffer.from(descriptionHash);
|
|
250
|
+
const nonceBuf = Buffer.alloc(8);
|
|
251
|
+
nonceBuf.writeBigUInt64LE(BigInt(nonce));
|
|
252
|
+
const programIds = getV3ProgramIds(network);
|
|
253
|
+
return PublicKey.findProgramAddressSync(
|
|
254
|
+
[Buffer.from(V3_SEEDS.ESCROW_V3), clientKey.toBuffer(), hashBuf, nonceBuf],
|
|
255
|
+
programIds.ESCROW
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
module.exports = {
|
|
260
|
+
// Program IDs
|
|
261
|
+
V3_DEVNET_PROGRAM_IDS,
|
|
262
|
+
V3_MAINNET_PROGRAM_IDS,
|
|
263
|
+
getV3ProgramIds,
|
|
264
|
+
V3_SEEDS,
|
|
265
|
+
|
|
266
|
+
// Utilities
|
|
267
|
+
hashAgentId,
|
|
268
|
+
hashName,
|
|
269
|
+
|
|
270
|
+
// PDA derivation
|
|
271
|
+
getGenesisPDA,
|
|
272
|
+
getV3ReputationAuthorityPDA,
|
|
273
|
+
getV3ValidationAuthorityPDA,
|
|
274
|
+
getV3MintTrackerPDA,
|
|
275
|
+
getNameRegistryPDA,
|
|
276
|
+
getLinkedWalletPDA,
|
|
277
|
+
getV3ReviewPDA,
|
|
278
|
+
getV3ReviewCounterPDA,
|
|
279
|
+
getV3AttestationPDA,
|
|
280
|
+
getV3EscrowPDA,
|
|
281
|
+
};
|