Skip to content

Commit 8805eb4

Browse files
authoredNov 14, 2024··
Merge pull request #313 from o-on-x/main
added working pumpfun.ts
2 parents a22e07f + 79637da commit 8805eb4

File tree

4 files changed

+358
-107
lines changed

4 files changed

+358
-107
lines changed
 

‎.env.example

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ X_SERVER_URL=
2929
XAI_API_KEY=
3030
XAI_MODEL=
3131

32+
#USE IMAGE GEN
33+
IMAGE_GEN= #TRUE
34+
3235
#Leave blank to use local embeddings
3336
USE_OPENAI_EMBEDDING= #TRUE
3437

‎packages/client-discord/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export class DiscordClient extends EventEmitter {
7373
this.runtime.registerAction(transcribe_media);
7474
this.runtime.registerAction(download_media);
7575

76+
7677
this.runtime.providers.push(channelStateProvider);
7778
this.runtime.providers.push(voiceStateProvider);
7879
}

‎packages/plugin-image-generation/src/index.ts

+62-11
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,34 @@ import {
99
} from "@ai16z/eliza/src/types.ts";
1010
import { generateCaption, generateImage } from "@ai16z/eliza/src/generation.ts";
1111

12+
import fs from 'fs';
13+
import path from 'path';
14+
15+
export function saveBase64Image(base64Data: string, filename: string): string {
16+
// Create generatedImages directory if it doesn't exist
17+
const imageDir = path.join(process.cwd(), 'generatedImages');
18+
if (!fs.existsSync(imageDir)) {
19+
fs.mkdirSync(imageDir, { recursive: true });
20+
}
21+
22+
// Remove the data:image/png;base64 prefix if it exists
23+
const base64Image = base64Data.replace(/^data:image\/\w+;base64,/, '');
24+
25+
// Create a buffer from the base64 string
26+
const imageBuffer = Buffer.from(base64Image, 'base64');
27+
28+
// Create full file path
29+
const filepath = path.join(imageDir, `${filename}.png`);
30+
31+
// Save the file
32+
fs.writeFileSync(filepath, imageBuffer);
33+
34+
return filepath;
35+
}
36+
1237
const imageGeneration: Action = {
1338
name: "GENERATE_IMAGE",
14-
similes: ["IMAGE_GENERATION", "IMAGE_GEN", "CREATE_IMAGE", "MAKE_PICTURE"],
39+
similes: ["IMAGE_GENERATION", "IMAGE_GEN", "CREATE_IMAGE", "MAKE_PICTURE",],
1540
description: "Generate an image to go along with the message.",
1641
validate: async (runtime: IAgentRuntime, message: Memory) => {
1742
const anthropicApiKeyOk = !!runtime.getSetting("ANTHROPIC_API_KEY");
@@ -58,36 +83,62 @@ const imageGeneration: Action = {
5883
);
5984
for (let i = 0; i < images.data.length; i++) {
6085
const image = images.data[i];
61-
elizaLogger.log(`Processing image ${i + 1}:`, image);
86+
87+
const base64Image = images.data[i];
88+
// Save the image and get filepath
89+
const filename = `generated_${Date.now()}_${i}`;
90+
const filepath = saveBase64Image(base64Image, filename);
91+
elizaLogger.log(`Processing image ${i + 1}:`, filename);
6292

63-
const caption = await generateCaption(
93+
//just dont even add a caption or a description just have it generate & send
94+
/*
95+
try {
96+
const imageService = runtime.getService(ServiceType.IMAGE_DESCRIPTION);
97+
if (imageService && typeof imageService.describeImage === 'function') {
98+
const caption = await imageService.describeImage({ imageUrl: filepath });
99+
captionText = caption.description;
100+
captionTitle = caption.title;
101+
}
102+
} catch (error) {
103+
elizaLogger.error("Caption generation failed, using default caption:", error);
104+
}*/
105+
106+
const caption = "...";
107+
/*= await generateCaption(
64108
{
65109
imageUrl: image,
66110
},
67111
runtime
68-
);
112+
);*/
113+
114+
res.push({ image: filepath, caption: "..."});//caption.title });
69115

70116
elizaLogger.log(
71117
`Generated caption for image ${i + 1}:`,
72-
caption.title
118+
"..."//caption.title
73119
);
74-
res.push({ image: image, caption: caption.title });
120+
//res.push({ image: image, caption: caption.title });
75121

76122
callback(
77123
{
78-
text: caption.description,
124+
text: "...",//caption.description,
79125
attachments: [
80126
{
81127
id: crypto.randomUUID(),
82-
url: image,
128+
url: filepath,
83129
title: "Generated image",
84130
source: "imageGeneration",
85-
description: caption.title,
86-
text: caption.description,
131+
description: "...",//caption.title,
132+
text: "...",//caption.description,
87133
},
88134
],
89135
},
90-
[]
136+
[
137+
{
138+
attachment: filepath,
139+
name: `${filename}.png`
140+
}
141+
]
91142
);
92143
}
93144
} else {

‎packages/plugin-solana/src/actions/pumpfun.ts

+292-96
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { AnchorProvider } from "@coral-xyz/anchor";
22
import { Wallet } from "@coral-xyz/anchor";
3+
import { generateImage } from "@ai16z/eliza/src/generation.ts";
34
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
45
import {
56
CreateTokenMetadata,
@@ -14,40 +15,53 @@ import settings from "@ai16z/eliza/src/settings.ts";
1415
import {
1516
ActionExample,
1617
Content,
18+
HandlerCallback,
1719
IAgentRuntime,
1820
Memory,
21+
ModelClass,
22+
State,
1923
type Action,
2024
} 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+
2135

2236
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;
2942
};
30-
allowOffCurve: boolean;
43+
buyAmountSol: string | number;
3144
}
3245

46+
3347
export function isCreateAndBuyContent(
3448
runtime: IAgentRuntime,
3549
content: any
3650
): content is CreateAndBuyContent {
51+
console.log("Content for create & buy", content)
3752
return (
38-
typeof content.deployerPrivateKey === "string" &&
3953
typeof content.tokenMetadata === "object" &&
4054
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" &&
4159
(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")
4861
);
4962
}
5063

64+
5165
export const createAndBuyToken = async ({
5266
deployer,
5367
mint,
@@ -88,6 +102,9 @@ export const createAndBuyToken = async ({
88102
priorityFee,
89103
commitment
90104
);
105+
106+
console.log("Create Results: ", createResults);
107+
91108
if (createResults.success) {
92109
console.log(
93110
"Success:",
@@ -111,9 +128,21 @@ export const createAndBuyToken = async ({
111128
} else {
112129
console.log(`${deployer.publicKey.toBase58()}:`, amount);
113130
}
131+
132+
return {
133+
success: true,
134+
ca: mint.publicKey.toBase58(),
135+
creator: deployer.publicKey.toBase58()
136+
};
137+
114138
} else {
115139
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+
}
117146
};
118147

119148
export const buyToken = async ({
@@ -213,6 +242,7 @@ export const sellToken = async ({
213242
};
214243

215244
const promptConfirmation = async (): Promise<boolean> => {
245+
return true;
216246
if (typeof window !== "undefined" && typeof window.confirm === "function") {
217247
return window.confirm(
218248
"Confirm the creation and purchase of the token?"
@@ -221,109 +251,275 @@ const promptConfirmation = async (): Promise<boolean> => {
221251
return true;
222252
};
223253

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+
224287
export default {
225288
name: "CREATE_AND_BUY_TOKEN",
226289
similes: ["CREATE_AND_PURCHASE_TOKEN", "DEPLOY_AND_BUY_TOKEN"],
227290
validate: async (runtime: IAgentRuntime, message: Memory) => {
228-
return isCreateAndBuyContent(runtime, message.content);
291+
292+
return true;//return isCreateAndBuyContent(runtime, message.content);
229293
},
230294
description:
231295
"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+
} */
264358

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);
266365

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(/^data:image\/[a-z]+;base64,/, '');
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);
271383
}
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+
};
272394

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);
285406

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+
}
294495
},
295496

296497
examples: [
297498
[
298499
{
299500
user: "{{user1}}",
300501
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+
}
313504
},
314505
{
315506
user: "{{user2}}",
316507
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",
318509
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+
,
329525
} as Action;

0 commit comments

Comments
 (0)
Please sign in to comment.