1
1
import { AnchorProvider } from "@coral-xyz/anchor" ;
2
2
import { Wallet } from "@coral-xyz/anchor" ;
3
+ import { generateImage } from "@ai16z/eliza/src/generation.ts" ;
3
4
import { Connection , Keypair , PublicKey } from "@solana/web3.js" ;
4
5
import {
5
6
CreateTokenMetadata ,
@@ -14,40 +15,53 @@ import settings from "@ai16z/eliza/src/settings.ts";
14
15
import {
15
16
ActionExample ,
16
17
Content ,
18
+ HandlerCallback ,
17
19
IAgentRuntime ,
18
20
Memory ,
21
+ ModelClass ,
22
+ State ,
19
23
type Action ,
20
24
} from "@ai16z/eliza/src/types.ts" ;
25
+ import { composeContext } from "@ai16z/eliza/src/context.ts" ;
26
+ import { generateObject } from "@ai16z/eliza/src/generation.ts" ;
27
+
28
+ import {
29
+ walletProvider ,
30
+ //WalletProvider,
31
+ } from "../providers/wallet.ts" ;
32
+
33
+ import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes/index.js" ;
34
+
21
35
22
36
export interface CreateAndBuyContent extends Content {
23
- deployerPrivateKey : string ;
24
- tokenMetadata : CreateTokenMetadata ;
25
- buyAmountSol : string | number ;
26
- priorityFee : {
27
- unitLimit : number ;
28
- unitPrice : number ;
37
+ tokenMetadata : {
38
+ name : string ;
39
+ symbol : string ;
40
+ description : string ;
41
+ image_description : string ;
29
42
} ;
30
- allowOffCurve : boolean ;
43
+ buyAmountSol : string | number ;
31
44
}
32
45
46
+
33
47
export function isCreateAndBuyContent (
34
48
runtime : IAgentRuntime ,
35
49
content : any
36
50
) : content is CreateAndBuyContent {
51
+ console . log ( "Content for create & buy" , content )
37
52
return (
38
- typeof content . deployerPrivateKey === "string" &&
39
53
typeof content . tokenMetadata === "object" &&
40
54
content . tokenMetadata !== null &&
55
+ typeof content . tokenMetadata . name === "string" &&
56
+ typeof content . tokenMetadata . symbol === "string" &&
57
+ typeof content . tokenMetadata . description === "string" &&
58
+ typeof content . tokenMetadata . image_description === "string" &&
41
59
( typeof content . buyAmountSol === "string" ||
42
- typeof content . buyAmountSol === "number" ) &&
43
- typeof content . priorityFee === "object" &&
44
- content . priorityFee !== null &&
45
- typeof content . priorityFee . unitLimit === "number" &&
46
- typeof content . priorityFee . unitPrice === "number" &&
47
- typeof content . allowOffCurve === "boolean"
60
+ typeof content . buyAmountSol === "number" )
48
61
) ;
49
62
}
50
63
64
+
51
65
export const createAndBuyToken = async ( {
52
66
deployer,
53
67
mint,
@@ -88,6 +102,9 @@ export const createAndBuyToken = async ({
88
102
priorityFee ,
89
103
commitment
90
104
) ;
105
+
106
+ console . log ( "Create Results: " , createResults ) ;
107
+
91
108
if ( createResults . success ) {
92
109
console . log (
93
110
"Success:" ,
@@ -111,9 +128,21 @@ export const createAndBuyToken = async ({
111
128
} else {
112
129
console . log ( `${ deployer . publicKey . toBase58 ( ) } :` , amount ) ;
113
130
}
131
+
132
+ return {
133
+ success : true ,
134
+ ca : mint . publicKey . toBase58 ( ) ,
135
+ creator : deployer . publicKey . toBase58 ( )
136
+ } ;
137
+
114
138
} else {
115
139
console . log ( "Create and Buy failed" ) ;
116
- }
140
+ return {
141
+ success : false ,
142
+ ca : mint . publicKey . toBase58 ( ) ,
143
+ error : createResults . error || "Transaction failed"
144
+ } ;
145
+ }
117
146
} ;
118
147
119
148
export const buyToken = async ( {
@@ -213,6 +242,7 @@ export const sellToken = async ({
213
242
} ;
214
243
215
244
const promptConfirmation = async ( ) : Promise < boolean > => {
245
+ return true ;
216
246
if ( typeof window !== "undefined" && typeof window . confirm === "function" ) {
217
247
return window . confirm (
218
248
"Confirm the creation and purchase of the token?"
@@ -221,109 +251,275 @@ const promptConfirmation = async (): Promise<boolean> => {
221
251
return true ;
222
252
} ;
223
253
254
+ // Save the base64 data to a file
255
+ import * as fs from 'fs' ;
256
+ import * as path from 'path' ;
257
+
258
+
259
+ const pumpfunTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
260
+
261
+ Example response:
262
+ \`\`\`json
263
+ {
264
+ "tokenMetadata": {
265
+ "name": "Test Token",
266
+ "symbol": "TEST",
267
+ "description": "A test token",
268
+ "image_description": "create an image of a rabbit"
269
+ },
270
+ "buyAmountSol": "0.00069"
271
+ }
272
+ \`\`\`
273
+
274
+ {{recentMessages}}
275
+
276
+ Given the recent messages, extract or generate (come up with if not included) the following information about the requested token creation:
277
+ - Token name
278
+ - Token symbol
279
+ - Token description
280
+ - Token image description
281
+ - Amount of SOL to buy
282
+
283
+ Respond with a JSON markdown block containing only the extracted values.` ;
284
+
285
+
286
+
224
287
export default {
225
288
name : "CREATE_AND_BUY_TOKEN" ,
226
289
similes : [ "CREATE_AND_PURCHASE_TOKEN" , "DEPLOY_AND_BUY_TOKEN" ] ,
227
290
validate : async ( runtime : IAgentRuntime , message : Memory ) => {
228
- return isCreateAndBuyContent ( runtime , message . content ) ;
291
+
292
+ return true ; //return isCreateAndBuyContent(runtime, message.content);
229
293
} ,
230
294
description :
231
295
"Create a new token and buy a specified amount using SOL. Requires deployer private key, token metadata, buy amount in SOL, priority fee, and allowOffCurve flag." ,
232
- handler : async (
233
- runtime : IAgentRuntime ,
234
- message : Memory
235
- ) : Promise < boolean > => {
236
- const content = message . content ;
237
- if ( ! isCreateAndBuyContent ( runtime , content ) ) {
238
- console . error ( "Invalid content for CREATE_AND_BUY_TOKEN action." ) ;
239
- return false ;
240
- }
241
- const {
242
- deployerPrivateKey,
243
- tokenMetadata,
244
- buyAmountSol,
245
- priorityFee,
246
- allowOffCurve,
247
- } = content ;
248
-
249
- const privateKey = runtime . getSetting ( "WALLET_PRIVATE_KEY" ) ! ;
250
- const wallet = new Wallet (
251
- Keypair . fromSecretKey ( new Uint8Array ( JSON . parse ( privateKey ) ) )
252
- ) ;
253
- const connection = new Connection ( settings . RPC_URL ! ) ;
254
- const provider = new AnchorProvider ( connection , wallet , {
255
- commitment : "finalized" ,
256
- } ) ;
257
- const sdk = new PumpFunSDK ( provider ) ;
258
- const slippage = runtime . getSetting ( "SLIPPAGE" ) ;
259
-
260
- try {
261
- const deployerKeypair = Keypair . fromSecretKey (
262
- Uint8Array . from ( Buffer . from ( deployerPrivateKey , "base64" ) )
263
- ) ;
296
+ handler : async (
297
+ runtime : IAgentRuntime ,
298
+ message : Memory ,
299
+ state : State ,
300
+ _options : { [ key : string ] : unknown } ,
301
+ callback ?: HandlerCallback
302
+ ) : Promise < boolean > => {
303
+ console . log ( "Starting CREATE_AND_BUY_TOKEN handler..." ) ;
304
+
305
+ // Compose state if not provided
306
+ if ( ! state ) {
307
+ state = ( await runtime . composeState ( message ) ) as State ;
308
+ } else {
309
+ state = await runtime . updateRecentMessageState ( state ) ;
310
+ }
311
+
312
+ // Get wallet info for context
313
+ const walletInfo = await walletProvider . get ( runtime , message , state ) ;
314
+ state . walletInfo = walletInfo ;
315
+
316
+ // Generate structured content from natural language
317
+ const pumpContext = composeContext ( {
318
+ state,
319
+ template : pumpfunTemplate ,
320
+ } ) ;
321
+
322
+ const content = await generateObject ( {
323
+ runtime,
324
+ context : pumpContext ,
325
+ modelClass : ModelClass . LARGE ,
326
+ } ) ;
327
+
328
+ // Validate the generated content
329
+ if ( ! isCreateAndBuyContent ( runtime , content ) ) {
330
+ console . error ( "Invalid content for CREATE_AND_BUY_TOKEN action." ) ;
331
+ return false ;
332
+ }
333
+
334
+ const { tokenMetadata, buyAmountSol } = content ;
335
+ /*
336
+ // Generate image if tokenMetadata.file is empty or invalid
337
+ if (!tokenMetadata.file || tokenMetadata.file.length < 100) { // Basic validation
338
+ try {
339
+ const imageResult = await generateImage({
340
+ prompt: `logo for ${tokenMetadata.name} (${tokenMetadata.symbol}) token - ${tokenMetadata.description}`,
341
+ width: 512,
342
+ height: 512,
343
+ count: 1
344
+ }, runtime);
345
+
346
+ if (imageResult.success && imageResult.data && imageResult.data.length > 0) {
347
+ // Remove the "data:image/png;base64," prefix if present
348
+ tokenMetadata.file = imageResult.data[0].replace(/^data:image\/[a-z]+;base64,/, '');
349
+ } else {
350
+ console.error("Failed to generate image:", imageResult.error);
351
+ return false;
352
+ }
353
+ } catch (error) {
354
+ console.error("Error generating image:", error);
355
+ return false;
356
+ }
357
+ } */
264
358
265
- const mintKeypair = Keypair . generate ( ) ;
359
+ const imageResult = await generateImage ( {
360
+ prompt : `logo for ${ tokenMetadata . name } (${ tokenMetadata . symbol } ) token - ${ tokenMetadata . description } ` ,
361
+ width : 256 ,
362
+ height : 256 ,
363
+ count : 1
364
+ } , runtime ) ;
266
365
267
- const createAndBuyConfirmation = await promptConfirmation ( ) ;
268
- if ( ! createAndBuyConfirmation ) {
269
- console . log ( "Create and buy token canceled by user" ) ;
270
- return false ;
366
+
367
+ tokenMetadata . image_description = imageResult . data [ 0 ] . replace ( / ^ d a t a : i m a g e \/ [ a - z ] + ; b a s e 6 4 , / , '' ) ;
368
+
369
+
370
+
371
+
372
+ // Convert base64 string to Blob
373
+ const base64Data = tokenMetadata . image_description ;
374
+ const outputPath = path . join ( process . cwd ( ) , `generated_image_${ Date . now ( ) } .txt` ) ;
375
+ fs . writeFileSync ( outputPath , base64Data ) ;
376
+ console . log ( `Base64 data saved to: ${ outputPath } ` ) ;
377
+
378
+
379
+ const byteCharacters = atob ( base64Data ) ;
380
+ const byteNumbers = new Array ( byteCharacters . length ) ;
381
+ for ( let i = 0 ; i < byteCharacters . length ; i ++ ) {
382
+ byteNumbers [ i ] = byteCharacters . charCodeAt ( i ) ;
271
383
}
384
+ const byteArray = new Uint8Array ( byteNumbers ) ;
385
+ const blob = new Blob ( [ byteArray ] , { type : 'image/png' } ) ;
386
+
387
+ // Add the default decimals and convert file to Blob
388
+ const fullTokenMetadata : CreateTokenMetadata = {
389
+ name : tokenMetadata . name ,
390
+ symbol : tokenMetadata . symbol ,
391
+ description : tokenMetadata . description ,
392
+ file : blob ,
393
+ } ;
272
394
273
- // Execute Create and Buy
274
- await createAndBuyToken ( {
275
- deployer : deployerKeypair ,
276
- mint : mintKeypair ,
277
- tokenMetadata : tokenMetadata as CreateTokenMetadata ,
278
- buyAmountSol : BigInt ( buyAmountSol ) ,
279
- priorityFee : priorityFee as PriorityFee ,
280
- allowOffCurve : allowOffCurve as boolean ,
281
- sdk,
282
- connection,
283
- slippage,
284
- } ) ;
395
+ // Default priority fee for high network load
396
+ const priorityFee = {
397
+ unitLimit : 100_000_000 ,
398
+ unitPrice : 100_000
399
+ } ;
400
+ const slippage = "2000"
401
+ try {
402
+ // Get private key from settings and create deployer keypair
403
+ const privateKeyString = runtime . getSetting ( "WALLET_PRIVATE_KEY" ) ! ;
404
+ const secretKey = bs58 . decode ( privateKeyString ) ;
405
+ const deployerKeypair = Keypair . fromSecretKey ( secretKey ) ;
285
406
286
- console . log (
287
- `Token created and purchased successfully! View at: https://pump.fun/${ mintKeypair . publicKey . toBase58 ( ) } `
288
- ) ;
289
- return true ;
290
- } catch ( error ) {
291
- console . error ( "Error during create and buy token:" , error ) ;
292
- return false ;
293
- }
407
+ // Generate new mint keypair
408
+ const mintKeypair = Keypair . generate ( ) ;
409
+ console . log ( `Generated mint address: ${ mintKeypair . publicKey . toBase58 ( ) } ` ) ;
410
+
411
+ // Setup connection and SDK
412
+ const connection = new Connection ( settings . RPC_URL ! , {
413
+ commitment : "confirmed" ,
414
+ confirmTransactionInitialTimeout : 500000 , // 120 seconds
415
+ wsEndpoint : settings . RPC_URL ! . replace ( 'https' , 'wss' )
416
+ } ) ;
417
+
418
+ const wallet = new Wallet ( deployerKeypair ) ;
419
+ const provider = new AnchorProvider ( connection , wallet , {
420
+ commitment : "finalized"
421
+ } ) ;
422
+ const sdk = new PumpFunSDK ( provider ) ;
423
+ // const slippage = runtime.getSetting("SLIPPAGE");
424
+
425
+
426
+ const createAndBuyConfirmation = await promptConfirmation ( ) ;
427
+ if ( ! createAndBuyConfirmation ) {
428
+ console . log ( "Create and buy token canceled by user" ) ;
429
+ return false ;
430
+ }
431
+
432
+ // Convert SOL to lamports (1 SOL = 1_000_000_000 lamports)
433
+ const lamports = Math . floor ( Number ( buyAmountSol ) * 1_000_000_000 ) ;
434
+
435
+ console . log ( "Executing create and buy transaction..." ) ;
436
+ const result = await createAndBuyToken ( {
437
+ deployer : deployerKeypair ,
438
+ mint : mintKeypair ,
439
+ tokenMetadata : fullTokenMetadata ,
440
+ buyAmountSol : BigInt ( lamports ) ,
441
+ priorityFee,
442
+ allowOffCurve : false ,
443
+ sdk,
444
+ connection,
445
+ slippage,
446
+ } ) ;
447
+
448
+
449
+ if ( callback ) {
450
+ if ( result . success ) {
451
+ callback ( {
452
+ text : `Token ${ tokenMetadata . name } (${ tokenMetadata . symbol } ) created successfully!\nContract Address: ${ result . ca } \nCreator: ${ result . creator } \nView at: https://pump.fun/${ result . ca } ` ,
453
+ content : {
454
+ tokenInfo : {
455
+ symbol : tokenMetadata . symbol ,
456
+ address : result . ca ,
457
+ creator : result . creator ,
458
+ name : tokenMetadata . name ,
459
+ description : tokenMetadata . description ,
460
+ timestamp : Date . now ( )
461
+ }
462
+ }
463
+ } ) ;
464
+ } else {
465
+ callback ( {
466
+ text : `Failed to create token: ${ result . error } \nAttempted mint address: ${ result . ca } ` ,
467
+ content : {
468
+ error : result . error ,
469
+ mintAddress : result . ca
470
+ }
471
+ } ) ;
472
+ }
473
+ }
474
+ //await trustScoreDb.addToken(tokenInfo);
475
+ /*
476
+ // Update runtime state
477
+ await runtime.updateState({
478
+ ...state,
479
+ lastCreatedToken: tokenInfo
480
+ });
481
+ */
482
+ // Log success message with token view URL
483
+ const successMessage = `Token created and purchased successfully! View at: https://pump.fun/${ mintKeypair . publicKey . toBase58 ( ) } ` ;
484
+ console . log ( successMessage ) ;
485
+ return result . success ;
486
+ } catch ( error ) {
487
+ if ( callback ) {
488
+ callback ( {
489
+ text : `Error during token creation: ${ error . message } ` ,
490
+ content : { error : error . message }
491
+ } ) ;
492
+ }
493
+ return false ;
494
+ }
294
495
} ,
295
496
296
497
examples : [
297
498
[
298
499
{
299
500
user : "{{user1}}" ,
300
501
content : {
301
- deployerPrivateKey : "Base64EncodedPrivateKey" ,
302
- tokenMetadata : {
303
- name : "MyToken" ,
304
- symbol : "MTK" ,
305
- description : "My first token" ,
306
- file : "Base64EncodedFile" , // blob file of the image
307
- decimals : DEFAULT_DECIMALS ,
308
- } ,
309
- buyAmountSol : "1000000000" , // 1 SOL in lamports
310
- priorityFee : 1000 ,
311
- allowOffCurve : false ,
312
- } ,
502
+ text : "Create a new token called GLITCHIZA with symbol GLITCHIZA and generate a description about it. Also come up with a description for it to use for image generation .buy 0.00069 SOL worth."
503
+ }
313
504
} ,
314
505
{
315
506
user : "{{user2}}" ,
316
507
content : {
317
- text : "Creating and buying 1 SOL worth of MyToken... " ,
508
+ text : "Token GLITCHIZA (GLITCHIZA) created successfully!\nContract Address: 3kD5DN4bbA3nykb1abjS66VF7cYZkKdirX8bZ6ShJjBB\nCreator: 9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa\nView at: https://pump.fun/EugPwuZ8oUMWsYHeBGERWvELfLGFmA1taDtmY8uMeX6r " ,
318
509
action : "CREATE_AND_BUY_TOKEN" ,
319
- } ,
320
- } ,
321
- {
322
- user : "{{user2}}" ,
323
- content : {
324
- text : "Token created and purchased successfully! View at: https://pump.fun/MintPublicKey" ,
325
- } ,
326
- } ,
327
- ] ,
328
- ] as ActionExample [ ] [ ] ,
510
+ content : {
511
+ tokenInfo : {
512
+ symbol : "GLITCHIZA" ,
513
+ address : "EugPwuZ8oUMWsYHeBGERWvELfLGFmA1taDtmY8uMeX6r" ,
514
+ creator : "9jW8FPr6BSSsemWPV22UUCzSqkVdTp6HTyPqeqyuBbCa" ,
515
+ name : "GLITCHIZA" ,
516
+ description : "A GLITCHIZA token"
517
+ }
518
+ }
519
+ }
520
+ }
521
+ ]
522
+ ] as ActionExample [ ] [ ]
523
+
524
+ ,
329
525
} as Action ;
0 commit comments