@crabspace/cli 0.2.5 → 0.2.6
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/commands/init.js +52 -1
- package/lib/anchor.js +58 -0
- package/package.json +1 -1
package/commands/init.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { loadKeypair, signForAction } from '../lib/sign.js';
|
|
9
9
|
import { writeConfig, configExists, readConfig, getConfigDir } from '../lib/config.js';
|
|
10
|
-
import { mkdirSync, writeFileSync, existsSync } from 'fs';
|
|
10
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
|
|
11
11
|
import { join } from 'path';
|
|
12
12
|
import { createInterface } from 'readline';
|
|
13
13
|
|
|
@@ -217,6 +217,31 @@ export async function init(args) {
|
|
|
217
217
|
};
|
|
218
218
|
writeConfig(config);
|
|
219
219
|
|
|
220
|
+
// Initialize IsnadIdentity on-chain if not already
|
|
221
|
+
try {
|
|
222
|
+
console.log('');
|
|
223
|
+
console.log('⛓️ Checking Identity PDA on-chain...');
|
|
224
|
+
const { Keypair: SolKeypair } = await import('@solana/web3.js');
|
|
225
|
+
const { initializeOnChain } = await import('../lib/anchor.js');
|
|
226
|
+
|
|
227
|
+
const keypairPath = args.keypair || '~/.config/solana/id.json';
|
|
228
|
+
const resolvedPath = keypairPath.replace('~', process.env.HOME);
|
|
229
|
+
const keypairJson = JSON.parse(readFileSync(resolvedPath, 'utf-8'));
|
|
230
|
+
const solKeypair = SolKeypair.fromSecretKey(Uint8Array.from(keypairJson));
|
|
231
|
+
|
|
232
|
+
const rpcUrl = args['rpc-url'] || 'https://api.mainnet-beta.solana.com';
|
|
233
|
+
const isnadHash = verifyData.isnad_hash || '0'.repeat(64);
|
|
234
|
+
|
|
235
|
+
const txSig = await initializeOnChain(solKeypair, isnadHash, rpcUrl);
|
|
236
|
+
if (txSig === 'already-initialized') {
|
|
237
|
+
console.log(' Identity PDA already exists.');
|
|
238
|
+
} else {
|
|
239
|
+
console.log(` On-chain init TX: ${txSig}`);
|
|
240
|
+
}
|
|
241
|
+
} catch (anchorErr) {
|
|
242
|
+
console.log(` ⚠️ On-chain init failed (non-blocking): ${anchorErr.message}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
220
245
|
console.log('');
|
|
221
246
|
console.log('✅ Config saved to ~/.crabspace/config.json');
|
|
222
247
|
console.log(` Agent: ${config.agentName} (id: ${config.agentId})`);
|
|
@@ -265,6 +290,32 @@ export async function init(args) {
|
|
|
265
290
|
console.log('📂 Scaffolding identity files...');
|
|
266
291
|
const paths = scaffoldIdentityFiles(config, data.bios_seed);
|
|
267
292
|
|
|
293
|
+
// 6. Initialize IsnadIdentity on-chain (non-blocking)
|
|
294
|
+
try {
|
|
295
|
+
console.log('');
|
|
296
|
+
console.log('⛓️ Initializing Identity PDA on-chain...');
|
|
297
|
+
const { Keypair: SolKeypair } = await import('@solana/web3.js');
|
|
298
|
+
const { initializeOnChain } = await import('../lib/anchor.js');
|
|
299
|
+
|
|
300
|
+
const keypairPath = args.keypair || '~/.config/solana/id.json';
|
|
301
|
+
const resolvedPath = keypairPath.replace('~', process.env.HOME);
|
|
302
|
+
const keypairJson = JSON.parse(readFileSync(resolvedPath, 'utf-8'));
|
|
303
|
+
const solKeypair = SolKeypair.fromSecretKey(Uint8Array.from(keypairJson));
|
|
304
|
+
|
|
305
|
+
const rpcUrl = args['rpc-url'] || 'https://api.mainnet-beta.solana.com';
|
|
306
|
+
const isnadHash = data.agent?.isnad_hash || '0'.repeat(64);
|
|
307
|
+
|
|
308
|
+
const txSig = await initializeOnChain(solKeypair, isnadHash, rpcUrl);
|
|
309
|
+
if (txSig === 'already-initialized') {
|
|
310
|
+
console.log(' Identity PDA already exists.');
|
|
311
|
+
} else {
|
|
312
|
+
console.log(` On-chain init TX: ${txSig}`);
|
|
313
|
+
}
|
|
314
|
+
} catch (anchorErr) {
|
|
315
|
+
console.log(` ⚠️ On-chain init failed (non-blocking): ${anchorErr.message}`);
|
|
316
|
+
console.log(` Fix: Ensure wallet has SOL, then run \`crabspace submit\` later.`);
|
|
317
|
+
}
|
|
318
|
+
|
|
268
319
|
console.log('');
|
|
269
320
|
console.log('✅ Agent registered successfully!');
|
|
270
321
|
console.log('');
|
package/lib/anchor.js
CHANGED
|
@@ -24,6 +24,11 @@ const LOG_WORK_DISCRIMINATOR = Buffer.from([
|
|
|
24
24
|
0xaa, 0x88, 0x30, 0x67, 0xdc, 0x86, 0xee, 0x73
|
|
25
25
|
]);
|
|
26
26
|
|
|
27
|
+
// Anchor discriminator for initialize (first 8 bytes of sha256("global:initialize"))
|
|
28
|
+
const INITIALIZE_DISCRIMINATOR = Buffer.from([
|
|
29
|
+
0xaf, 0xaf, 0x6d, 0x1f, 0x0d, 0x98, 0x9b, 0xed
|
|
30
|
+
]);
|
|
31
|
+
|
|
27
32
|
/**
|
|
28
33
|
* Derive the IsnadIdentity PDA for a given creator wallet.
|
|
29
34
|
* Seeds: ["isnad", creator_pubkey]
|
|
@@ -90,6 +95,59 @@ export async function anchorOnChain(keypair, workHash, rpcUrl = 'https://api.dev
|
|
|
90
95
|
return signature;
|
|
91
96
|
}
|
|
92
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Initialize an agent identity on-chain by calling the initialize instruction.
|
|
100
|
+
*
|
|
101
|
+
* @param {Keypair} keypair - The agent's Solana keypair (creator/payer)
|
|
102
|
+
* @param {string} headHash - Hex string of the initial work hash
|
|
103
|
+
* @param {string} rpcUrl - Solana RPC endpoint
|
|
104
|
+
* @returns {string} Transaction signature
|
|
105
|
+
*/
|
|
106
|
+
export async function initializeOnChain(keypair, headHash, rpcUrl = 'https://api.mainnet-beta.solana.com') {
|
|
107
|
+
const connection = new Connection(rpcUrl, 'confirmed');
|
|
108
|
+
const creatorPubkey = keypair.publicKey;
|
|
109
|
+
|
|
110
|
+
const [identityPda] = deriveIdentityPda(creatorPubkey);
|
|
111
|
+
|
|
112
|
+
const hashHex = (headHash || '0'.repeat(64)).replace('0x', '');
|
|
113
|
+
const hashBytes = Buffer.from(hashHex, 'hex');
|
|
114
|
+
const finalHash = new Uint8Array(32);
|
|
115
|
+
finalHash.set(new Uint8Array(hashBytes));
|
|
116
|
+
|
|
117
|
+
const data = Buffer.concat([INITIALIZE_DISCRIMINATOR, Buffer.from(finalHash)]);
|
|
118
|
+
|
|
119
|
+
const ix = new TransactionInstruction({
|
|
120
|
+
keys: [
|
|
121
|
+
{ pubkey: identityPda, isSigner: false, isWritable: true }, // identity account
|
|
122
|
+
{ pubkey: creatorPubkey, isSigner: true, isWritable: true }, // creator (signer, payer)
|
|
123
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, // system program
|
|
124
|
+
],
|
|
125
|
+
programId: PROGRAM_ID,
|
|
126
|
+
data,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const { blockhash } = await connection.getLatestBlockhash('confirmed');
|
|
130
|
+
const messageV0 = new TransactionMessage({
|
|
131
|
+
payerKey: creatorPubkey,
|
|
132
|
+
recentBlockhash: blockhash,
|
|
133
|
+
instructions: [ix],
|
|
134
|
+
}).compileToV0Message();
|
|
135
|
+
|
|
136
|
+
const tx = new VersionedTransaction(messageV0);
|
|
137
|
+
tx.sign([keypair]);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const signature = await connection.sendTransaction(tx, { skipPreflight: false });
|
|
141
|
+
await connection.confirmTransaction(signature, 'confirmed');
|
|
142
|
+
return signature;
|
|
143
|
+
} catch (e) {
|
|
144
|
+
if (e.message && e.message.includes('already in use')) {
|
|
145
|
+
return 'already-initialized';
|
|
146
|
+
}
|
|
147
|
+
throw e;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
93
151
|
/**
|
|
94
152
|
* Pay the CrabSpace work entry fee by transferring lamports to the treasury.
|
|
95
153
|
* Called automatically by submit.js when the API returns HTTP 402.
|