Skip to content

Commit 2ece0b4

Browse files
Dev/adding custon action (#1)
* env example updated * improving tokenmillaction * Add Monad Testnet support and enhance token deployment logic * Add Monad Testnet support and enhance token deployment logic * Implement token creation action and enhance parameter extraction logic * Add Twitter client plugin and update character bio * Add Supabase client integration and update token deployment callback * Refactor Supabase client integration and enhance file upload functionality * rebasing * Enhance token creation process by adding metadata handling and improve environment variable checks * Add database types and implement applications table operations in Supabase client * Add Application type and implement insert functionality for applications in Supabase
1 parent 7bbedfc commit 2ece0b4

File tree

14 files changed

+1132
-202
lines changed

14 files changed

+1132
-202
lines changed

.env.example

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,12 @@ TWITTER_APPROVAL_CHECK_INTERVAL=60000 # Default: 60 seconds
7272
#### Model Provider Configurations ####
7373
#######################################
7474

75-
7675
# Anthropic Configuration
7776
ANTHROPIC_API_KEY= # For Claude
7877
SMALL_ANTHROPIC_MODEL= # Default: claude-3-haiku-20240307
7978
MEDIUM_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022
8079
LARGE_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022
8180

82-
8381
######################################
8482
#### Crypto Plugin Configurations ####
8583
######################################
@@ -88,9 +86,17 @@ LARGE_ANTHROPIC_MODEL= # Default: claude-3-5-sonnet-20241022
8886
EVM_PRIVATE_KEY= # Add the "0x" prefix infront of your private key string
8987
EVM_PROVIDER_URL=
9088

91-
9289
############################################
9390
#### Custom Sherry Monad Configurations ####
9491
############################################
9592

93+
# Custom Sherry Monad Configuration
94+
TM_PROXY_ADDRESS= # Address of the Token Manager Proxy contract
95+
TM_FACTORY_ADDRESS= # Address of the Token Manager Factory contract
96+
WMONAD_ADDRESS= # Address of the Wrapped Monad contract
97+
98+
# Supabase Config
99+
SUPABASE_URL=
100+
SUPABASE_ANON_KEY=
101+
BUCKET_NAME=
96102

agent/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
"exec": "node --enable-source-maps --loader ts-node/esm src/index.ts"
1919
},
2020
"dependencies": {
21+
"@elizaos-plugins/client-twitter": "workspace:0.25.6-alpha.1",
2122
"@elizaos/client-direct": "workspace:*",
22-
"@elizaos/plugin-bootstrap": "workspace:*",
2323
"@elizaos/core": "workspace:*",
24+
"@elizaos/plugin-bootstrap": "workspace:*",
2425
"readline": "1.3.0",
2526
"ws": "8.18.0",
2627
"yargs": "17.7.2"

agent/src/index.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import {
1919
validateCharacterConfig,
2020
} from "@elizaos/core";
2121
import { defaultCharacter } from "./defaultCharacter.ts";
22-
2322
import { bootstrapPlugin } from "@elizaos/plugin-bootstrap";
23+
import { createTokenAndMarketAction } from "./tokenMillAction.ts";
2424

2525
import fs from "fs";
2626
import net from "net";
@@ -616,6 +616,7 @@ export async function createAgent(
616616
.filter(Boolean),
617617
providers: [],
618618
managers: [],
619+
actions: [createTokenAndMarketAction],
619620
fetch: logFetch,
620621
// verifiableInferenceAdapter,
621622
});
@@ -693,23 +694,23 @@ function initializeCache(
693694
}
694695

695696
async function findDatabaseAdapter(runtime: AgentRuntime) {
696-
const { adapters } = runtime;
697-
let adapter: Adapter | undefined;
698-
// if not found, default to sqlite
699-
if (adapters.length === 0) {
700-
const sqliteAdapterPlugin = await import('@elizaos-plugins/adapter-sqlite');
701-
const sqliteAdapterPluginDefault = sqliteAdapterPlugin.default;
702-
adapter = sqliteAdapterPluginDefault.adapters[0];
703-
if (!adapter) {
704-
throw new Error("Internal error: No database adapter found for default adapter-sqlite");
705-
}
706-
} else if (adapters.length === 1) {
707-
adapter = adapters[0];
708-
} else {
709-
throw new Error("Multiple database adapters found. You must have no more than one. Adjust your plugins configuration.");
697+
const { adapters } = runtime;
698+
let adapter: Adapter | undefined;
699+
// if not found, default to sqlite
700+
if (adapters.length === 0) {
701+
const sqliteAdapterPlugin = await import('@elizaos-plugins/adapter-sqlite');
702+
const sqliteAdapterPluginDefault = sqliteAdapterPlugin.default;
703+
adapter = sqliteAdapterPluginDefault.adapters[0];
704+
if (!adapter) {
705+
throw new Error("Internal error: No database adapter found for default adapter-sqlite");
706+
}
707+
} else if (adapters.length === 1) {
708+
adapter = adapters[0];
709+
} else {
710+
throw new Error("Multiple database adapters found. You must have no more than one. Adjust your plugins configuration.");
710711
}
711-
const adapterInterface = adapter?.init(runtime);
712-
return adapterInterface;
712+
const adapterInterface = adapter?.init(runtime);
713+
return adapterInterface;
713714
}
714715

715716
async function startAgent(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { Tables, InsertTables, UpdateTables } from '../utils/database.types'
2+
3+
export type Application = InsertTables<"applications">;

agent/src/tokenMillAction.ts

Lines changed: 133 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,28 @@ import {
66
type Action,
77
} from "@elizaos/core";
88
import { elizaLogger } from "@elizaos/core";
9-
import { parseAbi, encodeAbiParameters, createWalletClient } from 'viem';
9+
import { parseAbi, encodeAbiParameters, createWalletClient, createClient, createPublicClient, http } from 'viem';
10+
import { privateKeyToAccount } from 'viem/accounts'
1011
import { handleTokenParameters, formatTokenDetails, TokenParameters } from "./utils/tokenUtils";
11-
12-
//const walletClient = createWalletClient();
12+
import { monadTestnet } from "./utils/monadChain";
13+
import { processMetadata, insertApplication } from "./utils/supabase";
14+
import { Application } from "./interface/Applications";
1315

1416
export const createTokenAndMarketAction: Action = {
1517
name: "CREATE_TOKEN",
1618
similes: ["DEPLOY_TOKEN", "CREATE_ERC20"],
1719
description: "Create and deploy a new ERC-20 token with TokenMill",
1820

1921
validate: async (_agent: IAgentRuntime, _memory: Memory, _state?: State) => {
20-
// Extract token parameters from user message
22+
// Extract token parameters from user message - primarily name and symbol
2123
const tokenParams = handleTokenParameters(_memory.content.text);
22-
24+
elizaLogger.info("Catching token params")
25+
2326
if (!tokenParams) return false;
2427

28+
elizaLogger.info("Token params catched")
29+
elizaLogger.info(tokenParams)
30+
2531
if (_state) {
2632
_state.tokenParams = tokenParams;
2733
return true;
@@ -31,26 +37,26 @@ export const createTokenAndMarketAction: Action = {
3137

3238
handler: async (_agent: IAgentRuntime, _memory: Memory, _state?: State, _options?: any, _callback?: HandlerCallback) => {
3339
if (!_callback) throw new Error("Callback is required");
34-
40+
3541
// Get the token parameters, explicitly handle the null case
3642
let tokenParams: TokenParameters | null = null;
37-
43+
3844
if (_state?.tokenParams) {
3945
tokenParams = _state.tokenParams as TokenParameters;
4046
} else {
4147
tokenParams = handleTokenParameters(_memory.content.text);
4248
}
43-
49+
4450
// Check if tokenParams is null or undefined
4551
if (!tokenParams) {
46-
_callback({ text: "❌ Unable to extract token parameters from your request. Please provide details like name, symbol, and supply." });
52+
_callback({ text: "❌ Unable to extract token name from your request. Please provide a name for your token." });
4753
return false;
4854
}
4955

5056
// Environment variables
5157
const privateKey = process.env.EVM_PRIVATE_KEY;
52-
const TMFactoryAddress = process.env.TM_FACTORY_ADDRESS || '0x501ee2D4AA611C906F785e10cC868e145183FCE4'; // Default to Monad Testnet
53-
const WMONAD = process.env.WMONAD_ADDRESS || '0x760AfE86e5de5fa0Ee542fc7B7B713e1c5425701';
58+
const TMFactoryAddress = process.env.TM_FACTORY_ADDRESS;
59+
const WMONAD = process.env.WMONAD_ADDRESS;
5460

5561
if (!privateKey || !TMFactoryAddress || !WMONAD) {
5662
_callback({ text: "⚠️ Missing environment variables. Please check the configuration." });
@@ -60,14 +66,15 @@ export const createTokenAndMarketAction: Action = {
6066
try {
6167
_callback({ text: "🚀 Starting token creation process with TokenMill..." });
6268

63-
// Initialize blockchain service for token deployment
69+
// Initialize token deployment with extracted name and symbol
70+
// Other parameters are using default values
6471
const { name, symbol, totalSupply, decimals, creatorShare, stakingShare } = tokenParams;
65-
72+
6673
_callback({ text: `📝 Preparing token with name: ${name}, symbol: ${symbol}, supply: ${totalSupply}` });
6774

6875
// Prepare token parameters
6976
const parameters = {
70-
tokenType: 1n, // ERC20
77+
tokenType: 1n, // ERC20 as far as I know :p
7178
name: name,
7279
symbol: symbol,
7380
quoteToken: WMONAD as `0x${string}`,
@@ -79,28 +86,52 @@ export const createTokenAndMarketAction: Action = {
7986
args: encodeAbiParameters([{ type: 'uint256' }], [BigInt(decimals)]),
8087
};
8188

82-
_callback({ text: "🔄 Deploying token to blockchain..." });
89+
const processResult = await processMetadata(name, "0x1b1f2Bfc5e551b955F2a3F973876cEE917FB4d05");
90+
91+
elizaLogger.info(`Metadata hosted successfully - URL : ${processResult}`)
92+
_callback({ text: `Metadata hosted successfully - URL : ${processResult}` });
93+
//_callback({ text: "🔄 Deploying token to blockchain..." });
94+
//_callback({ text: "https://app.sherry.social/action?url=https://app.sherry.social/api/examples/token-mill-swap" })
95+
96+
let app: Application = {
97+
api_url: processResult,
98+
email: "gilberts@sherry.social",
99+
explanation: `Buy Tokens deployed on Token Mill`,
100+
name: `Simplify the way users buy tokens`,
101+
project_name: "Monad AI Agent",
102+
state: "approved",
103+
telegram: "@gilbertsahumada",
104+
twitter: "@gilbertsahumada",
105+
created_at: new Date().toISOString(),
106+
updated_at: new Date().toISOString(),
107+
}
108+
109+
const result = await insertApplication(app);
83110

111+
_callback({ text: `✅ Token deployed successfully! ID : ${result.id_application}` })
112+
/*
84113
try {
85114
// In a real implementation, you would integrate with Hardhat or another library
86115
// to deploy the token. This is a simplified placeholder.
87116
const result = await deployToken(TMFactoryAddress, parameters);
88-
89-
_callback({
90-
text: `✅ Token deployed successfully!\n\n` +
91-
`📋 Token Details:\n` +
92-
`- Name: ${name}\n` +
93-
`- Symbol: ${symbol}\n` +
94-
`- Contract: ${result.tokenAddress}\n` +
95-
`- Market: ${result.marketAddress}\n\n` +
96-
`You can now interact with your token using the mini-app at: https://tokenmill.xyz/tokens/${result.tokenAddress}`
117+
118+
_callback({
119+
text: `✅ Token deployed successfully!\n\n` +
120+
`📋 Token Details:\n` +
121+
`- Name: ${name}\n` +
122+
`- Symbol: ${symbol}\n` +
123+
`- Contract: ${result.tokenAddress}\n` +
124+
`- Market: ${result.marketAddress}\n\n` +
125+
`You can now interact with your token using the mini-app at: https://tokenmill.xyz/tokens/${result.tokenAddress}`
97126
});
127+
98128
return true;
99129
} catch (error: any) {
100130
elizaLogger.error("Token Deployment Error:", error);
101131
_callback({ text: `❌ Failed to deploy token: ${error.message || error}` });
102132
return false;
103133
}
134+
*/
104135
} catch (error: any) {
105136
elizaLogger.error("Token Creation Error:", error);
106137
_callback({ text: "❌ An error occurred while creating the token." });
@@ -132,12 +163,82 @@ export const createTokenAndMarketAction: Action = {
132163
]
133164
};
134165

135-
// Placeholder for actual token deployment functionality
136-
async function deployToken(factoryAddress: string, parameters: any): Promise<{tokenAddress: string, marketAddress: string}> {
137-
// This would actually call the blockchain in a real implementation
138-
// For this example, we're just returning placeholder values
139-
return {
140-
tokenAddress: `0x${Math.random().toString(16).substring(2, 42)}`,
141-
marketAddress: `0x${Math.random().toString(16).substring(2, 42)}`,
142-
};
166+
async function deployToken(factoryAddress: string, parameters: any): Promise<{ tokenAddress: string, marketAddress: string }> {
167+
try {
168+
169+
const privateKey = process.env.EVM_PRIVATE_KEY;
170+
171+
if (!privateKey) {
172+
throw new Error('EVM_PRIVATE_KEY environment variable is missing.');
173+
}
174+
175+
// create account from private key
176+
const account = privateKeyToAccount(privateKey as `0x${string}`);
177+
178+
// Init wallet client from account
179+
const walletClient = createWalletClient({
180+
account,
181+
chain: monadTestnet,
182+
transport: http()
183+
});
184+
185+
// Init public client
186+
const publicClient = await createPublicClient({
187+
chain: monadTestnet,
188+
transport: http()
189+
});
190+
191+
// TokenMill factory ABI for the createMarketAndToken function
192+
const abi = parseAbi([
193+
'function createMarketAndToken((uint96 tokenType, string name, string symbol, address quoteToken, uint256 totalSupply, uint16 creatorShare, uint16 stakingShare, uint256[] bidPrices, uint256[] askPrices, bytes args) parameters) external returns (address baseToken, address market)'
194+
]);
195+
196+
// Validate price arrays
197+
if (parameters.bidPrices.length !== parameters.askPrices.length ||
198+
parameters.bidPrices.length < 2 ||
199+
parameters.bidPrices.length > 101) {
200+
throw new Error('Price arrays have invalid length.');
201+
}
202+
203+
elizaLogger.info('Simulating createMarketAndToken to get expected return values...');
204+
205+
// First simulate the contract call to get the return values
206+
const { result, request } = await publicClient.simulateContract({
207+
address: factoryAddress as `0x${string}`,
208+
abi,
209+
functionName: 'createMarketAndToken',
210+
args: [parameters],
211+
account: walletClient.account,
212+
});
213+
214+
// Extract the expected return values from simulation
215+
const expectedToken = result[0];
216+
const expectedMarket = result[1];
217+
218+
elizaLogger.info(`Expected Token Address: ${expectedToken}`);
219+
elizaLogger.info(`Expected Market Address: ${expectedMarket}`);
220+
221+
// Execute the actual transaction
222+
elizaLogger.info('Executing token deployment transaction...');
223+
const tx = await walletClient.writeContract(request);
224+
225+
elizaLogger.info(`Transaction sent: ${tx}`);
226+
const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
227+
elizaLogger.info(`Transaction confirmed in block ${receipt.blockNumber}`);
228+
229+
return {
230+
tokenAddress: expectedToken,
231+
marketAddress: expectedMarket
232+
};
233+
234+
} catch (error: any) {
235+
elizaLogger.error('Token deployment error:', error);
236+
if (error.cause) {
237+
elizaLogger.error('Error details:', error.cause);
238+
}
239+
throw new Error(`Failed to deploy token: ${error.message || 'Unknown error'}`);
240+
}
143241
}
242+
243+
244+

0 commit comments

Comments
 (0)