Skip to content

Commit

Permalink
Merge pull request #757 from asDNSk/main
Browse files Browse the repository at this point in the history
fix: follow-up improvements for ICP token creation (PR #357)
  • Loading branch information
lalalune authored Dec 1, 2024
2 parents e9eac3e + c1190a3 commit 66afaa1
Show file tree
Hide file tree
Showing 18 changed files with 292 additions and 170 deletions.
1 change: 1 addition & 0 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@ai16z/plugin-solana": "workspace:*",
"@ai16z/plugin-0g": "workspace:*",
"@ai16z/plugin-starknet": "workspace:*",
"@ai16z/plugin-icp": "workspace:*",
"@ai16z/plugin-tee": "workspace:*",
"@ai16z/plugin-coinbase": "workspace:*",
"readline": "1.3.0",
Expand Down
205 changes: 135 additions & 70 deletions packages/plugin-icp/src/actions/createToken.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { composeContext } from "@ai16z/eliza/src/context";
import { generateObject } from "@ai16z/eliza/src/generation";
import {
composeContext,
generateImage,
generateText,
generateObject,
} from "@ai16z/eliza";
import {
ActionExample,
HandlerCallback,
Expand All @@ -8,56 +12,31 @@ import {
ModelClass,
State,
type Action,
} from "@ai16z/eliza/src/types";
} from "@ai16z/eliza";
import { idlFactory } from "../canisters/pick-pump/index.did";
import { _SERVICE } from "../canisters/pick-pump/index.did.d";
import { ActorCreator, CreateMemeTokenArg } from "../types";
import { unwrapOption, wrapOption } from "../utils/common/types/options";
import { unwrapRustResultMap } from "../utils/common/types/results";
import { icpWalletProvider } from "../providers/wallet";

const createTokenTemplate = `Respond with a JSON markdown block containing only the extracted values. Use null for any values that cannot be determined.
Based on the user's description, generate appropriate values for a new token:
- Create a suitable token name
- Generate a 3-4 letter symbol based on the name
- Write a clear description
- Use "https://icptoken.default.logo" as default logo URL
- Set other fields to null
Example response:
\`\`\`json
{
"name": "My ICP Token",
"symbol": "MIT",
"description": "A fun meme token on ICP",
"logo": "https://icptoken.default.logo",
"website": null,
"twitter": null,
"telegram": null
}
\`\`\`
{{recentMessages}}
Generate appropriate token information based on the user's description.
Respond with a JSON markdown block containing only the generated values.`;
import { uploadFileToWeb3Storage } from "../apis/uploadFile";
import { createTokenTemplate, logoPromptTemplate } from './prompts/token';
import { CANISTER_IDS } from '../constants/canisters';

async function createTokenTransaction(
creator: ActorCreator,
tokenInfo: CreateMemeTokenArg
): Promise<any> {
) {
const actor: _SERVICE = await creator(
idlFactory,
"bn4fo-iyaaa-aaaap-akp6a-cai"
CANISTER_IDS.PICK_PUMP
);

const result = await actor.create_token({
...tokenInfo,
name: tokenInfo.name ?? "My ICP Token",
symbol: tokenInfo.symbol ?? "MIT",
description: tokenInfo.description ?? "A fun meme token on ICP",
logo: "https://icptoken.default.logo",
name: tokenInfo.name,
symbol: tokenInfo.symbol,
description: tokenInfo.description,
logo: tokenInfo.logo,
twitter: wrapOption(tokenInfo.twitter),
website: wrapOption(tokenInfo.website),
telegram: wrapOption(tokenInfo.telegram),
Expand All @@ -67,6 +46,12 @@ async function createTokenTransaction(
result,
(ok) => ({
...ok,
id: ok.id.toString(),
created_at: ok.created_at.toString(),
available_token: ok.available_token.toString(),
volume_24h: ok.volume_24h.toString(),
last_tx_time: ok.last_tx_time.toString(),
market_cap_icp: ok.market_cap_icp.toString(),
twitter: unwrapOption(ok.twitter),
website: unwrapOption(ok.website),
telegram: unwrapOption(ok.telegram),
Expand All @@ -77,21 +62,82 @@ async function createTokenTransaction(
);
}

async function generateTokenLogo(
description: string,
runtime: IAgentRuntime
): Promise<string | null> {
const logoPrompt = `Create a fun and memorable logo for a cryptocurrency token with these characteristics: ${description}. The logo should be simple, iconic, and suitable for a meme token. Style: minimal, bold colors, crypto-themed.`;

const result = await generateImage(
{
prompt: logoPrompt,
width: 512,
height: 512,
count: 1,
},
runtime as any
);

if (result.success && result.data && result.data.length > 0) {
return result.data[0];
}

return null;
}

export const executeCreateToken: Action = {
name: "CREATE_TOKEN",
similes: ["CREATE_COIN", "MINT_TOKEN", "DEPLOY_TOKEN", "CREATE_ICP_TOKEN"],
similes: [
"CREATE_PICKPUMP_TOKEN",
"MINT_PICKPUMP",
"PICKPUMP_TOKEN",
"PP_TOKEN",
"PICKPUMP发币",
"PP发币",
"在PICKPUMP上发币",
"PICKPUMP代币",
],
description:
"Create a new meme token on PickPump platform (Internet Computer). This action helps users create and launch tokens specifically on the PickPump platform.",
validate: async (runtime: IAgentRuntime, message: Memory) => {
console.log("Message:", message);
return true;
const keywords = [
"pickpump",
"pp",
"皮克帮",
"token",
"coin",
"代币",
"币",
"create",
"mint",
"launch",
"deploy",
"创建",
"发行",
"铸造",
];

const messageText = (
typeof message.content === "string"
? message.content
: message.content.text || ""
).toLowerCase();

return keywords.some((keyword) => messageText.includes(keyword.toLowerCase()));
},
description: "Create a new token on Internet Computer.",
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_options: { [key: string]: unknown },
state: State | undefined,
_options: { [key: string]: unknown } | undefined,
callback?: HandlerCallback
): Promise<boolean> => {
): Promise<void> => {
callback?.({
text: "🔄 Creating meme token...",
action: "CREATE_TOKEN",
type: "processing",
});

if (!state) {
state = (await runtime.composeState(message)) as State;
} else {
Expand All @@ -109,20 +155,28 @@ export const executeCreateToken: Action = {
modelClass: ModelClass.LARGE,
});

console.log("Response:", response);
const logoPromptContext = composeContext({
state,
template: logoPromptTemplate.replace(
"{{description}}",
response.description
),
});

// Validate required fields
if (
!response.name ||
!response.symbol ||
!response.description ||
!response.logo
) {
const responseMsg = {
text: "I need the token name, symbol, description, and logo URL to create a token",
};
callback?.(responseMsg);
return true;
const logoPrompt = await generateText({
runtime,
context: logoPromptContext,
modelClass: ModelClass.SMALL,
});

const logo = await generateTokenLogo(logoPrompt, runtime);
if (!logo) {
throw new Error("Failed to generate token logo");
}

const logoUploadResult = await uploadFileToWeb3Storage(logo);
if (!logoUploadResult.urls?.gateway) {
throw new Error("Failed to upload logo to Web3Storage");
}

try {
Expand All @@ -131,50 +185,61 @@ export const executeCreateToken: Action = {
message,
state
);

const creator = wallet.createActor;
const createTokenResult = await createTokenTransaction(creator, {
name: response.name,
symbol: response.symbol,
description: response.description,
website: response.website,
twitter: response.twitter,
telegram: response.telegram,
logo: logoUploadResult.urls.gateway,
});

console.log("Token created successfully:", createTokenResult);
const responseMsg = {
text: `Token ${response.name} (${response.symbol}) created successfully on ICP!`,
text: `✨ Created new meme token:\n🪙 ${response.name} (${response.symbol})\n📝 ${response.description}`,
data: createTokenResult,
action: "CREATE_TOKEN",
type: "success",
};

callback?.(responseMsg);
return true;
} catch (error) {
console.error("Error creating token:", error);
} catch (error: any) {
const responseMsg = {
text: `Failed to create token: ${error.message}`,
action: "CREATE_TOKEN",
type: "error",
};
callback?.(responseMsg);
return false;
}
},
examples: [
[
{
user: "{{user1}}",
content: "I want to create a token for dog lovers",
content: "I want to create a space cat token on PickPump",
},
{
user: "{{user2}}",
content: {
text: "Creating new ICP token WOOF...",
text: "Creating space cat token on PickPump...",
action: "CREATE_TOKEN",
},
},
{
user: "{{user2}}",
content: {
text: "Token created successfully on Internet Computer!",
text: "✨ Token created successfully!",
},
},
],
[
{
user: "{{user1}}",
content: "Help me create a pizza-themed funny token on PP",
},
{
user: "{{user2}}",
content: {
text: "Creating pizza token on PickPump...",
action: "CREATE_TOKEN",
},
},
],
Expand Down
34 changes: 34 additions & 0 deletions packages/plugin-icp/src/actions/prompts/token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const createTokenTemplate = `Based on the user's description, generate creative and memorable values for a new meme token on PickPump:
User's idea: "{{recentMessages}}"
Please generate:
1. A catchy and fun token name that reflects the theme
2. A 3-4 letter symbol based on the name (all caps)
3. An engaging and humorous description (include emojis)
4. Set other fields to null
Example response:
\`\`\`json
{
"name": "CatLaser",
"symbol": "PAWS",
"description": "The first meme token powered by feline laser-chasing energy! Watch your investment zoom around like a red dot! 😺🔴✨",
"logo": null,
"website": null,
"twitter": null,
"telegram": null
}
\`\`\`
Generate appropriate meme token information based on the user's description.
Respond with a JSON markdown block containing only the generated values.`;

export const logoPromptTemplate = `Based on this token idea: "{{description}}", create a detailed prompt for generating a logo image.
The prompt should describe visual elements, style, and mood for the logo.
Focus on making it memorable and suitable for a cryptocurrency token.
Keep the response short and specific.
Respond with only the prompt text, no additional formatting.
Example for a dog-themed token:
"A playful cartoon dog face with a cryptocurrency symbol on its collar, using vibrant colors and bold outlines, crypto-themed minimal style"`;
Loading

0 comments on commit 66afaa1

Please sign in to comment.