@brainai/satp-client 2.0.2 → 2.0.4
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 +67 -23
- package/examples/runtime-policy-adapter.js +33 -13
- package/package.json +6 -4
- package/src/borsh-reader.d.ts +5 -0
- package/src/borsh-reader.js +25 -11
- package/src/index.d.ts +210 -1
- package/src/index.js +23 -0
- package/src/runtime-policy-adapter.js +293 -0
- package/src/signer-policy.js +210 -0
- package/src/v3-idl-discriminators.js +17 -0
- package/src/v3-pda.d.ts +26 -0
- package/src/v3-pda.js +56 -5
- package/src/v3-sdk.d.ts +85 -4
- package/src/v3-sdk.js +379 -13
package/src/v3-sdk.js
CHANGED
|
@@ -20,6 +20,11 @@ const {
|
|
|
20
20
|
getV3ReviewCounterPDA,
|
|
21
21
|
getV3AttestationPDA,
|
|
22
22
|
getV3EscrowPDA,
|
|
23
|
+
getAssociatedTokenAddress,
|
|
24
|
+
getV3EscrowVaultATA,
|
|
25
|
+
V3_DEVNET_TOKEN_MINTS,
|
|
26
|
+
SPL_TOKEN_PROGRAM_ID,
|
|
27
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
23
28
|
} = require('./v3-pda');
|
|
24
29
|
|
|
25
30
|
const DEVNET_RPC = 'https://api.devnet.solana.com';
|
|
@@ -37,13 +42,10 @@ function normalizeSDKOptions(opts = {}) {
|
|
|
37
42
|
: { rpcUrl: opts };
|
|
38
43
|
}
|
|
39
44
|
const rpcUrl = normalized.rpcUrl || normalized.url || normalized.endpoint;
|
|
40
|
-
if (!normalized.network && isMainnetRpc(rpcUrl)) {
|
|
41
|
-
throw new Error('Mainnet RPC requires network=mainnet, but SATP V3 mainnet program IDs are not configured');
|
|
42
|
-
}
|
|
43
45
|
return {
|
|
44
46
|
...normalized,
|
|
45
47
|
rpcUrl,
|
|
46
|
-
network: normalized.network || 'devnet',
|
|
48
|
+
network: normalized.network || (isMainnetRpc(rpcUrl) ? 'mainnet' : 'devnet'),
|
|
47
49
|
};
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -79,6 +81,93 @@ function serializeVecString(arr) {
|
|
|
79
81
|
return Buffer.concat([count, ...parts]);
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
function hashDescription(descriptionOrHash) {
|
|
85
|
+
return Buffer.isBuffer(descriptionOrHash) && descriptionOrHash.length === 32
|
|
86
|
+
? descriptionOrHash
|
|
87
|
+
: crypto.createHash('sha256')
|
|
88
|
+
.update(typeof descriptionOrHash === 'string' ? descriptionOrHash : Buffer.from(descriptionOrHash))
|
|
89
|
+
.digest();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeCurrency(currency) {
|
|
93
|
+
const value = String(currency || 'SOL').toUpperCase();
|
|
94
|
+
if (value !== 'SOL' && value !== 'USDC') {
|
|
95
|
+
throw new Error('Unsupported escrow currency: expected SOL or USDC');
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolveTokenMint(currency, mint) {
|
|
101
|
+
if (mint) return new PublicKey(mint);
|
|
102
|
+
if (currency === 'USDC') return V3_DEVNET_TOKEN_MINTS.USDC;
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function createAssociatedTokenAccountIdempotentInstruction(payer, owner, mint) {
|
|
107
|
+
const payerKey = new PublicKey(payer);
|
|
108
|
+
const ownerKey = new PublicKey(owner);
|
|
109
|
+
const mintKey = new PublicKey(mint);
|
|
110
|
+
const [ata] = getAssociatedTokenAddress(ownerKey, mintKey);
|
|
111
|
+
|
|
112
|
+
return new TransactionInstruction({
|
|
113
|
+
programId: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
114
|
+
keys: [
|
|
115
|
+
{ pubkey: payerKey, isSigner: true, isWritable: true },
|
|
116
|
+
{ pubkey: ata, isSigner: false, isWritable: true },
|
|
117
|
+
{ pubkey: ownerKey, isSigner: false, isWritable: false },
|
|
118
|
+
{ pubkey: mintKey, isSigner: false, isWritable: false },
|
|
119
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
120
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
121
|
+
],
|
|
122
|
+
data: Buffer.from([1]), // SPL Associated Token Account: CreateIdempotent
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeTokenOpts(opts = {}) {
|
|
127
|
+
const currency = normalizeCurrency(opts.currency);
|
|
128
|
+
const mint = resolveTokenMint(currency, opts.mint);
|
|
129
|
+
return {
|
|
130
|
+
currency,
|
|
131
|
+
mint,
|
|
132
|
+
tokenDecimals: opts.tokenDecimals == null ? 6 : opts.tokenDecimals,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function maybeAddAtaInstruction(tx, enabled, payer, owner, mint) {
|
|
137
|
+
if (enabled) {
|
|
138
|
+
tx.add(createAssociatedTokenAccountIdempotentInstruction(payer, owner, mint));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function readOptionPubkey(data, offset) {
|
|
143
|
+
const hasValue = data[offset] === 1;
|
|
144
|
+
offset += 1;
|
|
145
|
+
if (!hasValue) return { value: null, offset };
|
|
146
|
+
const value = new PublicKey(data.slice(offset, offset + 32)).toBase58();
|
|
147
|
+
return { value, offset: offset + 32 };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function readOptionU8(data, offset) {
|
|
151
|
+
const hasValue = data[offset] === 1;
|
|
152
|
+
offset += 1;
|
|
153
|
+
if (!hasValue) return { value: null, offset };
|
|
154
|
+
return { value: data[offset], offset: offset + 1 };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readOptionBytes32(data, offset) {
|
|
158
|
+
const hasValue = data[offset] === 1;
|
|
159
|
+
offset += 1;
|
|
160
|
+
if (!hasValue) return { value: null, offset };
|
|
161
|
+
return { value: Buffer.from(data.slice(offset, offset + 32)).toString('hex'), offset: offset + 32 };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function readOptionI64(data, offset) {
|
|
165
|
+
const hasValue = data[offset] === 1;
|
|
166
|
+
offset += 1;
|
|
167
|
+
if (!hasValue) return { value: null, offset };
|
|
168
|
+
return { value: Number(data.readBigInt64LE(offset)), offset: offset + 8 };
|
|
169
|
+
}
|
|
170
|
+
|
|
82
171
|
class SATPV3SDK {
|
|
83
172
|
/**
|
|
84
173
|
* @param {object} opts
|
|
@@ -1084,11 +1173,12 @@ class SATPV3SDK {
|
|
|
1084
1173
|
* @param {PublicKey|string} client - Client wallet (signer + payer)
|
|
1085
1174
|
* @param {PublicKey|string} agentWallet - Agent's wallet to receive funds
|
|
1086
1175
|
* @param {string} agentId - Agent identifier (for Genesis Record lookup)
|
|
1087
|
-
* @param {number} amount - Lamports to escrow
|
|
1176
|
+
* @param {number} amount - Lamports or token base units to escrow
|
|
1088
1177
|
* @param {string|Buffer} descriptionOrHash - Job description string (will be SHA-256 hashed) or 32-byte hash buffer
|
|
1089
1178
|
* @param {number} deadline - Unix timestamp deadline
|
|
1090
1179
|
* @param {number} [nonce=0] - Nonce for uniqueness (multiple escrows between same parties)
|
|
1091
|
-
* @param {object} [opts={}] - Optional trust requirements
|
|
1180
|
+
* @param {object} [opts={}] - Optional trust requirements and currency selection
|
|
1181
|
+
* @param {'SOL'|'USDC'} [opts.currency='SOL'] - Escrow funding currency
|
|
1092
1182
|
* @param {number} [opts.minVerificationLevel=0] - Minimum verification level (0-5)
|
|
1093
1183
|
* @param {boolean} [opts.requireBorn=false] - Require agent to have completed burn-to-become
|
|
1094
1184
|
* @param {PublicKey|string} [opts.arbiter] - Arbiter for dispute resolution (defaults to client)
|
|
@@ -1097,9 +1187,20 @@ class SATPV3SDK {
|
|
|
1097
1187
|
async buildCreateEscrow(client, agentWallet, agentId, amount, descriptionOrHash, deadline, nonce = 0, opts = {}) {
|
|
1098
1188
|
const clientKey = new PublicKey(client);
|
|
1099
1189
|
const agentWalletKey = new PublicKey(agentWallet);
|
|
1100
|
-
const descriptionHash =
|
|
1101
|
-
|
|
1102
|
-
|
|
1190
|
+
const descriptionHash = hashDescription(descriptionOrHash);
|
|
1191
|
+
const currency = normalizeCurrency(opts.currency);
|
|
1192
|
+
if (currency === 'USDC') {
|
|
1193
|
+
return this.buildCreateUsdcEscrow(
|
|
1194
|
+
clientKey,
|
|
1195
|
+
agentWalletKey,
|
|
1196
|
+
agentId,
|
|
1197
|
+
amount,
|
|
1198
|
+
descriptionHash,
|
|
1199
|
+
deadline,
|
|
1200
|
+
nonce,
|
|
1201
|
+
opts
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1103
1204
|
|
|
1104
1205
|
const [escrowPDA] = getV3EscrowPDA(clientKey, descriptionHash, nonce, this.network);
|
|
1105
1206
|
const [agentIdentityPDA] = getGenesisPDA(agentId, this.network);
|
|
@@ -1150,6 +1251,135 @@ class SATPV3SDK {
|
|
|
1150
1251
|
return { transaction: tx, escrowPDA, descriptionHash };
|
|
1151
1252
|
}
|
|
1152
1253
|
|
|
1254
|
+
/**
|
|
1255
|
+
* Build createEscrow transaction for USDC/SPL token escrow.
|
|
1256
|
+
* Includes idempotent vault ATA creation by default; client source ATA must
|
|
1257
|
+
* exist and hold enough token base units for transfer_checked to succeed.
|
|
1258
|
+
* @param {PublicKey|string} client - Client wallet (signer + payer)
|
|
1259
|
+
* @param {PublicKey|string} agentWallet - Agent's wallet to receive funds
|
|
1260
|
+
* @param {string} agentId - Agent identifier (for Genesis Record lookup)
|
|
1261
|
+
* @param {number} amount - Token base units to escrow
|
|
1262
|
+
* @param {string|Buffer} descriptionOrHash - Job description or 32-byte hash
|
|
1263
|
+
* @param {number} deadline - Unix timestamp deadline
|
|
1264
|
+
* @param {number} [nonce=0] - Nonce for uniqueness
|
|
1265
|
+
* @param {object} [opts={}]
|
|
1266
|
+
* @param {PublicKey|string} [opts.mint] - SPL mint; defaults to devnet USDC
|
|
1267
|
+
* @param {number} [opts.tokenDecimals=6] - Mint decimals used by transfer_checked
|
|
1268
|
+
* @param {boolean} [opts.createVaultAta=true] - Add idempotent vault ATA creation
|
|
1269
|
+
* @returns {{ transaction: Transaction, escrowPDA: PublicKey, descriptionHash: Buffer, currency: 'USDC', mint: PublicKey, clientTokenAccount: PublicKey, vaultTokenAccount: PublicKey }}
|
|
1270
|
+
*/
|
|
1271
|
+
async buildCreateUsdcEscrow(client, agentWallet, agentId, amount, descriptionOrHash, deadline, nonce = 0, opts = {}) {
|
|
1272
|
+
const clientKey = new PublicKey(client);
|
|
1273
|
+
const agentWalletKey = new PublicKey(agentWallet);
|
|
1274
|
+
const descriptionHash = hashDescription(descriptionOrHash);
|
|
1275
|
+
const mintKey = resolveTokenMint('USDC', opts.mint);
|
|
1276
|
+
const tokenDecimals = opts.tokenDecimals == null ? 6 : opts.tokenDecimals;
|
|
1277
|
+
const { escrowPDA, vaultATA } = getV3EscrowVaultATA(clientKey, descriptionHash, nonce, mintKey, this.network);
|
|
1278
|
+
const [agentIdentityPDA] = getGenesisPDA(agentId, this.network);
|
|
1279
|
+
const [clientTokenAccount] = getAssociatedTokenAddress(clientKey, mintKey);
|
|
1280
|
+
|
|
1281
|
+
const arbiterKey = opts.arbiter ? new PublicKey(opts.arbiter) : clientKey;
|
|
1282
|
+
const minVerificationLevel = opts.minVerificationLevel || 0;
|
|
1283
|
+
const requireBorn = opts.requireBorn || false;
|
|
1284
|
+
|
|
1285
|
+
const amountBuf = Buffer.alloc(8);
|
|
1286
|
+
amountBuf.writeBigUInt64LE(BigInt(amount));
|
|
1287
|
+
const deadlineBuf = Buffer.alloc(8);
|
|
1288
|
+
deadlineBuf.writeBigInt64LE(BigInt(deadline));
|
|
1289
|
+
const nonceBuf = Buffer.alloc(8);
|
|
1290
|
+
nonceBuf.writeBigUInt64LE(BigInt(nonce));
|
|
1291
|
+
|
|
1292
|
+
const data = Buffer.concat([
|
|
1293
|
+
anchorDiscriminator('create_usdc_escrow'),
|
|
1294
|
+
serializeString(agentId),
|
|
1295
|
+
amountBuf,
|
|
1296
|
+
descriptionHash,
|
|
1297
|
+
deadlineBuf,
|
|
1298
|
+
nonceBuf,
|
|
1299
|
+
Buffer.from([minVerificationLevel]),
|
|
1300
|
+
Buffer.from([requireBorn ? 1 : 0]),
|
|
1301
|
+
Buffer.from([tokenDecimals]),
|
|
1302
|
+
]);
|
|
1303
|
+
|
|
1304
|
+
const tx = new Transaction();
|
|
1305
|
+
if (opts.createVaultAta !== false) {
|
|
1306
|
+
tx.add(createAssociatedTokenAccountIdempotentInstruction(clientKey, escrowPDA, mintKey));
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
tx.add(new TransactionInstruction({
|
|
1310
|
+
programId: this.programIds.ESCROW,
|
|
1311
|
+
keys: [
|
|
1312
|
+
{ pubkey: clientKey, isSigner: true, isWritable: true },
|
|
1313
|
+
{ pubkey: agentWalletKey, isSigner: false, isWritable: false },
|
|
1314
|
+
{ pubkey: agentIdentityPDA, isSigner: false, isWritable: false },
|
|
1315
|
+
{ pubkey: arbiterKey, isSigner: false, isWritable: false },
|
|
1316
|
+
{ pubkey: escrowPDA, isSigner: false, isWritable: true },
|
|
1317
|
+
{ pubkey: mintKey, isSigner: false, isWritable: false },
|
|
1318
|
+
{ pubkey: clientTokenAccount, isSigner: false, isWritable: true },
|
|
1319
|
+
{ pubkey: vaultATA, isSigner: false, isWritable: true },
|
|
1320
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1321
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
1322
|
+
],
|
|
1323
|
+
data,
|
|
1324
|
+
}));
|
|
1325
|
+
|
|
1326
|
+
tx.feePayer = clientKey;
|
|
1327
|
+
tx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
|
|
1328
|
+
|
|
1329
|
+
return {
|
|
1330
|
+
transaction: tx,
|
|
1331
|
+
escrowPDA,
|
|
1332
|
+
descriptionHash,
|
|
1333
|
+
currency: 'USDC',
|
|
1334
|
+
mint: mintKey,
|
|
1335
|
+
clientTokenAccount,
|
|
1336
|
+
vaultTokenAccount: vaultATA,
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Build release transaction for USDC/SPL escrow funds.
|
|
1342
|
+
* Adds idempotent agent ATA creation by default so the destination account
|
|
1343
|
+
* exists before the escrow program performs transfer_checked.
|
|
1344
|
+
* @param {PublicKey|string} client
|
|
1345
|
+
* @param {PublicKey|string} agent
|
|
1346
|
+
* @param {PublicKey|string} escrowPDA
|
|
1347
|
+
* @param {object} [opts={}]
|
|
1348
|
+
* @param {PublicKey|string} [opts.mint]
|
|
1349
|
+
* @param {boolean} [opts.createAgentAta=true]
|
|
1350
|
+
* @returns {{ transaction: Transaction, mint: PublicKey, vaultTokenAccount: PublicKey, agentTokenAccount: PublicKey }}
|
|
1351
|
+
*/
|
|
1352
|
+
async buildUsdcEscrowRelease(client, agent, escrowPDA, opts = {}) {
|
|
1353
|
+
const clientKey = new PublicKey(client);
|
|
1354
|
+
const agentKey = new PublicKey(agent);
|
|
1355
|
+
const escrowKey = new PublicKey(escrowPDA);
|
|
1356
|
+
const { mint } = normalizeTokenOpts({ ...opts, currency: 'USDC' });
|
|
1357
|
+
const [vaultTokenAccount] = getAssociatedTokenAddress(escrowKey, mint);
|
|
1358
|
+
const [agentTokenAccount] = getAssociatedTokenAddress(agentKey, mint);
|
|
1359
|
+
|
|
1360
|
+
const tx = new Transaction();
|
|
1361
|
+
maybeAddAtaInstruction(tx, opts.createAgentAta !== false, clientKey, agentKey, mint);
|
|
1362
|
+
|
|
1363
|
+
tx.add(new TransactionInstruction({
|
|
1364
|
+
programId: this.programIds.ESCROW,
|
|
1365
|
+
keys: [
|
|
1366
|
+
{ pubkey: escrowKey, isSigner: false, isWritable: true },
|
|
1367
|
+
{ pubkey: clientKey, isSigner: true, isWritable: false },
|
|
1368
|
+
{ pubkey: agentKey, isSigner: false, isWritable: false },
|
|
1369
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
1370
|
+
{ pubkey: vaultTokenAccount, isSigner: false, isWritable: true },
|
|
1371
|
+
{ pubkey: agentTokenAccount, isSigner: false, isWritable: true },
|
|
1372
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1373
|
+
],
|
|
1374
|
+
data: anchorDiscriminator('release_usdc'),
|
|
1375
|
+
}));
|
|
1376
|
+
|
|
1377
|
+
tx.feePayer = clientKey;
|
|
1378
|
+
tx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
|
|
1379
|
+
|
|
1380
|
+
return { transaction: tx, mint, vaultTokenAccount, agentTokenAccount };
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1153
1383
|
/**
|
|
1154
1384
|
* Build submitWork transaction (agent submits work proof).
|
|
1155
1385
|
* @param {PublicKey|string} agent - Agent wallet (signer)
|
|
@@ -1190,7 +1420,11 @@ class SATPV3SDK {
|
|
|
1190
1420
|
* @param {PublicKey|string} escrowPDA - Escrow account address
|
|
1191
1421
|
* @returns {{ transaction: Transaction }}
|
|
1192
1422
|
*/
|
|
1193
|
-
async buildEscrowRelease(client, agent, escrowPDA) {
|
|
1423
|
+
async buildEscrowRelease(client, agent, escrowPDA, opts = {}) {
|
|
1424
|
+
if (normalizeCurrency(opts.currency) === 'USDC') {
|
|
1425
|
+
return this.buildUsdcEscrowRelease(client, agent, escrowPDA, opts);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1194
1428
|
const clientKey = new PublicKey(client);
|
|
1195
1429
|
const agentKey = new PublicKey(agent);
|
|
1196
1430
|
const escrowKey = new PublicKey(escrowPDA);
|
|
@@ -1223,6 +1457,11 @@ class SATPV3SDK {
|
|
|
1223
1457
|
* @returns {{ transaction: Transaction }}
|
|
1224
1458
|
*/
|
|
1225
1459
|
async buildPartialRelease(client, agent, escrowPDA, amount) {
|
|
1460
|
+
const opts = arguments.length >= 5 ? arguments[4] || {} : {};
|
|
1461
|
+
if (normalizeCurrency(opts.currency) === 'USDC') {
|
|
1462
|
+
return this.buildPartialUsdcEscrowRelease(client, agent, escrowPDA, amount, opts);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1226
1465
|
const clientKey = new PublicKey(client);
|
|
1227
1466
|
const agentKey = new PublicKey(agent);
|
|
1228
1467
|
const escrowKey = new PublicKey(escrowPDA);
|
|
@@ -1250,13 +1489,38 @@ class SATPV3SDK {
|
|
|
1250
1489
|
return { transaction: tx };
|
|
1251
1490
|
}
|
|
1252
1491
|
|
|
1492
|
+
/**
|
|
1493
|
+
* Build partial release transaction for USDC/SPL escrow funds.
|
|
1494
|
+
* @param {PublicKey|string} client
|
|
1495
|
+
* @param {PublicKey|string} agent
|
|
1496
|
+
* @param {PublicKey|string} escrowPDA
|
|
1497
|
+
* @param {number} amount
|
|
1498
|
+
* @param {object} [opts={}]
|
|
1499
|
+
* @returns {{ transaction: Transaction, mint: PublicKey, vaultTokenAccount: PublicKey, agentTokenAccount: PublicKey }}
|
|
1500
|
+
*/
|
|
1501
|
+
async buildPartialUsdcEscrowRelease(client, agent, escrowPDA, amount, opts = {}) {
|
|
1502
|
+
const result = await this.buildUsdcEscrowRelease(client, agent, escrowPDA, {
|
|
1503
|
+
...opts,
|
|
1504
|
+
createAgentAta: opts.createAgentAta,
|
|
1505
|
+
});
|
|
1506
|
+
const amountBuf = Buffer.alloc(8);
|
|
1507
|
+
amountBuf.writeBigUInt64LE(BigInt(amount));
|
|
1508
|
+
const ix = result.transaction.instructions[result.transaction.instructions.length - 1];
|
|
1509
|
+
ix.data = Buffer.concat([anchorDiscriminator('partial_release_usdc'), amountBuf]);
|
|
1510
|
+
return result;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1253
1513
|
/**
|
|
1254
1514
|
* Build cancel transaction (client cancels after deadline, gets refund).
|
|
1255
1515
|
* @param {PublicKey|string} client - Client wallet (signer)
|
|
1256
1516
|
* @param {PublicKey|string} escrowPDA - Escrow account address
|
|
1257
1517
|
* @returns {{ transaction: Transaction }}
|
|
1258
1518
|
*/
|
|
1259
|
-
async buildCancelEscrow(client, escrowPDA) {
|
|
1519
|
+
async buildCancelEscrow(client, escrowPDA, opts = {}) {
|
|
1520
|
+
if (normalizeCurrency(opts.currency) === 'USDC') {
|
|
1521
|
+
return this.buildUsdcCancelEscrow(client, escrowPDA, opts);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1260
1524
|
const clientKey = new PublicKey(client);
|
|
1261
1525
|
const escrowKey = new PublicKey(escrowPDA);
|
|
1262
1526
|
|
|
@@ -1278,6 +1542,41 @@ class SATPV3SDK {
|
|
|
1278
1542
|
return { transaction: tx };
|
|
1279
1543
|
}
|
|
1280
1544
|
|
|
1545
|
+
/**
|
|
1546
|
+
* Build cancel transaction for USDC/SPL escrow refund.
|
|
1547
|
+
* @param {PublicKey|string} client
|
|
1548
|
+
* @param {PublicKey|string} escrowPDA
|
|
1549
|
+
* @param {object} [opts={}]
|
|
1550
|
+
* @returns {{ transaction: Transaction, mint: PublicKey, vaultTokenAccount: PublicKey, clientTokenAccount: PublicKey }}
|
|
1551
|
+
*/
|
|
1552
|
+
async buildUsdcCancelEscrow(client, escrowPDA, opts = {}) {
|
|
1553
|
+
const clientKey = new PublicKey(client);
|
|
1554
|
+
const escrowKey = new PublicKey(escrowPDA);
|
|
1555
|
+
const { mint } = normalizeTokenOpts({ ...opts, currency: 'USDC' });
|
|
1556
|
+
const [vaultTokenAccount] = getAssociatedTokenAddress(escrowKey, mint);
|
|
1557
|
+
const [clientTokenAccount] = getAssociatedTokenAddress(clientKey, mint);
|
|
1558
|
+
|
|
1559
|
+
const tx = new Transaction();
|
|
1560
|
+
maybeAddAtaInstruction(tx, opts.createClientAta === true, clientKey, clientKey, mint);
|
|
1561
|
+
tx.add(new TransactionInstruction({
|
|
1562
|
+
programId: this.programIds.ESCROW,
|
|
1563
|
+
keys: [
|
|
1564
|
+
{ pubkey: escrowKey, isSigner: false, isWritable: true },
|
|
1565
|
+
{ pubkey: clientKey, isSigner: true, isWritable: true },
|
|
1566
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
1567
|
+
{ pubkey: vaultTokenAccount, isSigner: false, isWritable: true },
|
|
1568
|
+
{ pubkey: clientTokenAccount, isSigner: false, isWritable: true },
|
|
1569
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1570
|
+
],
|
|
1571
|
+
data: anchorDiscriminator('cancel_usdc'),
|
|
1572
|
+
}));
|
|
1573
|
+
|
|
1574
|
+
tx.feePayer = clientKey;
|
|
1575
|
+
tx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
|
|
1576
|
+
|
|
1577
|
+
return { transaction: tx, mint, vaultTokenAccount, clientTokenAccount };
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1281
1580
|
/**
|
|
1282
1581
|
* Build raiseDispute transaction (either client or agent).
|
|
1283
1582
|
* @param {PublicKey|string} signer - Client or agent wallet (signer)
|
|
@@ -1321,7 +1620,11 @@ class SATPV3SDK {
|
|
|
1321
1620
|
* @param {number} clientAmount - Lamports to refund to client
|
|
1322
1621
|
* @returns {{ transaction: Transaction }}
|
|
1323
1622
|
*/
|
|
1324
|
-
async buildResolveDispute(arbiter, agent, clientWallet, escrowPDA, agentAmount, clientAmount) {
|
|
1623
|
+
async buildResolveDispute(arbiter, agent, clientWallet, escrowPDA, agentAmount, clientAmount, opts = {}) {
|
|
1624
|
+
if (normalizeCurrency(opts.currency) === 'USDC') {
|
|
1625
|
+
return this.buildUsdcResolveDispute(arbiter, agent, clientWallet, escrowPDA, agentAmount, clientAmount, opts);
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1325
1628
|
const arbiterKey = new PublicKey(arbiter);
|
|
1326
1629
|
const agentKey = new PublicKey(agent);
|
|
1327
1630
|
const clientKey = new PublicKey(clientWallet);
|
|
@@ -1353,6 +1656,58 @@ class SATPV3SDK {
|
|
|
1353
1656
|
return { transaction: tx };
|
|
1354
1657
|
}
|
|
1355
1658
|
|
|
1659
|
+
/**
|
|
1660
|
+
* Build resolveDispute transaction for USDC/SPL escrow funds.
|
|
1661
|
+
* Adds idempotent recipient ATA creation by default.
|
|
1662
|
+
* @param {PublicKey|string} arbiter
|
|
1663
|
+
* @param {PublicKey|string} agent
|
|
1664
|
+
* @param {PublicKey|string} clientWallet
|
|
1665
|
+
* @param {PublicKey|string} escrowPDA
|
|
1666
|
+
* @param {number} agentAmount
|
|
1667
|
+
* @param {number} clientAmount
|
|
1668
|
+
* @param {object} [opts={}]
|
|
1669
|
+
* @returns {{ transaction: Transaction, mint: PublicKey, vaultTokenAccount: PublicKey, agentTokenAccount: PublicKey, clientTokenAccount: PublicKey }}
|
|
1670
|
+
*/
|
|
1671
|
+
async buildUsdcResolveDispute(arbiter, agent, clientWallet, escrowPDA, agentAmount, clientAmount, opts = {}) {
|
|
1672
|
+
const arbiterKey = new PublicKey(arbiter);
|
|
1673
|
+
const agentKey = new PublicKey(agent);
|
|
1674
|
+
const clientKey = new PublicKey(clientWallet);
|
|
1675
|
+
const escrowKey = new PublicKey(escrowPDA);
|
|
1676
|
+
const { mint } = normalizeTokenOpts({ ...opts, currency: 'USDC' });
|
|
1677
|
+
const [vaultTokenAccount] = getAssociatedTokenAddress(escrowKey, mint);
|
|
1678
|
+
const [agentTokenAccount] = getAssociatedTokenAddress(agentKey, mint);
|
|
1679
|
+
const [clientTokenAccount] = getAssociatedTokenAddress(clientKey, mint);
|
|
1680
|
+
|
|
1681
|
+
const agentAmtBuf = Buffer.alloc(8);
|
|
1682
|
+
agentAmtBuf.writeBigUInt64LE(BigInt(agentAmount));
|
|
1683
|
+
const clientAmtBuf = Buffer.alloc(8);
|
|
1684
|
+
clientAmtBuf.writeBigUInt64LE(BigInt(clientAmount));
|
|
1685
|
+
|
|
1686
|
+
const tx = new Transaction();
|
|
1687
|
+
maybeAddAtaInstruction(tx, opts.createAgentAta !== false, arbiterKey, agentKey, mint);
|
|
1688
|
+
maybeAddAtaInstruction(tx, opts.createClientAta !== false, arbiterKey, clientKey, mint);
|
|
1689
|
+
tx.add(new TransactionInstruction({
|
|
1690
|
+
programId: this.programIds.ESCROW,
|
|
1691
|
+
keys: [
|
|
1692
|
+
{ pubkey: escrowKey, isSigner: false, isWritable: true },
|
|
1693
|
+
{ pubkey: arbiterKey, isSigner: true, isWritable: false },
|
|
1694
|
+
{ pubkey: agentKey, isSigner: false, isWritable: false },
|
|
1695
|
+
{ pubkey: clientKey, isSigner: false, isWritable: false },
|
|
1696
|
+
{ pubkey: mint, isSigner: false, isWritable: false },
|
|
1697
|
+
{ pubkey: vaultTokenAccount, isSigner: false, isWritable: true },
|
|
1698
|
+
{ pubkey: agentTokenAccount, isSigner: false, isWritable: true },
|
|
1699
|
+
{ pubkey: clientTokenAccount, isSigner: false, isWritable: true },
|
|
1700
|
+
{ pubkey: SPL_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
1701
|
+
],
|
|
1702
|
+
data: Buffer.concat([anchorDiscriminator('resolve_dispute_usdc'), agentAmtBuf, clientAmtBuf]),
|
|
1703
|
+
}));
|
|
1704
|
+
|
|
1705
|
+
tx.feePayer = arbiterKey;
|
|
1706
|
+
tx.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
|
|
1707
|
+
|
|
1708
|
+
return { transaction: tx, mint, vaultTokenAccount, agentTokenAccount, clientTokenAccount };
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1356
1711
|
/**
|
|
1357
1712
|
* Build extendDeadline transaction (client extends escrow deadline).
|
|
1358
1713
|
* Only when Active. New deadline must be strictly after current.
|
|
@@ -1438,6 +1793,10 @@ class SATPV3SDK {
|
|
|
1438
1793
|
const descriptionHash = data.slice(offset, offset + 32); offset += 32;
|
|
1439
1794
|
const deadline = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
1440
1795
|
const nonce = Number(data.readBigUInt64LE(offset)); offset += 8;
|
|
1796
|
+
const currencyByte = data[offset]; offset += 1;
|
|
1797
|
+
const tokenMintResult = readOptionPubkey(data, offset); offset = tokenMintResult.offset;
|
|
1798
|
+
const tokenVaultResult = readOptionPubkey(data, offset); offset = tokenVaultResult.offset;
|
|
1799
|
+
const tokenDecimalsResult = readOptionU8(data, offset); offset = tokenDecimalsResult.offset;
|
|
1441
1800
|
const statusByte = data[offset]; offset += 1;
|
|
1442
1801
|
const minVerificationLevel = data[offset]; offset += 1;
|
|
1443
1802
|
const requireBorn = data[offset] === 1; offset += 1;
|
|
@@ -1487,6 +1846,7 @@ class SATPV3SDK {
|
|
|
1487
1846
|
const bump = data[offset]; offset += 1;
|
|
1488
1847
|
|
|
1489
1848
|
const STATUS_MAP = ['Active', 'WorkSubmitted', 'Released', 'Cancelled', 'Disputed', 'Resolved'];
|
|
1849
|
+
const CURRENCY_MAP = ['SOL', 'USDC'];
|
|
1490
1850
|
|
|
1491
1851
|
return {
|
|
1492
1852
|
pda: escrowKey.toBase58(),
|
|
@@ -1499,6 +1859,11 @@ class SATPV3SDK {
|
|
|
1499
1859
|
descriptionHash: Buffer.from(descriptionHash).toString('hex'),
|
|
1500
1860
|
deadline,
|
|
1501
1861
|
nonce,
|
|
1862
|
+
currency: CURRENCY_MAP[currencyByte] || `Unknown(${currencyByte})`,
|
|
1863
|
+
currencyCode: currencyByte,
|
|
1864
|
+
tokenMint: tokenMintResult.value,
|
|
1865
|
+
tokenVault: tokenVaultResult.value,
|
|
1866
|
+
tokenDecimals: tokenDecimalsResult.value,
|
|
1502
1867
|
status: STATUS_MAP[statusByte] || `Unknown(${statusByte})`,
|
|
1503
1868
|
statusCode: statusByte,
|
|
1504
1869
|
minVerificationLevel,
|
|
@@ -1623,7 +1988,7 @@ class SATPV3SDK {
|
|
|
1623
1988
|
const faceMint = new PublicKey(data.slice(offset, offset + 32)); offset += 32;
|
|
1624
1989
|
const faceBurnTx = readString();
|
|
1625
1990
|
const genesisRecord = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
1626
|
-
|
|
1991
|
+
const isActive = data[offset] === 1; offset += 1;
|
|
1627
1992
|
const authority = new PublicKey(data.slice(offset, offset + 32)); offset += 32;
|
|
1628
1993
|
|
|
1629
1994
|
// Option<Pubkey> — Borsh: 0x00 = None (1 byte only), 0x01 + 32 bytes = Some
|
|
@@ -1657,6 +2022,7 @@ class SATPV3SDK {
|
|
|
1657
2022
|
faceBurnTx: faceBurnTx || null,
|
|
1658
2023
|
genesisRecord,
|
|
1659
2024
|
isBorn,
|
|
2025
|
+
isActive,
|
|
1660
2026
|
authority: authority.toBase58(),
|
|
1661
2027
|
pendingAuthority,
|
|
1662
2028
|
reputationScore,
|