Skip to content

Commit

Permalink
refactor: enhance prediction actions and blockchain interactions
Browse files Browse the repository at this point in the history
- Update listPredictions to improve prediction display with ID and formatting
- Modify prepareBet and placeBet actions to streamline bet preparation and placement
- Improve bet parameter extraction and transaction handling
- Enhance user instructions and response messages for betting process
- Adjust prediction resolver to handle errors more gracefully
- Refine prediction template to focus specifically on weather predictions
  • Loading branch information
nicky-ru committed Jan 29, 2025
1 parent 5f54b5c commit e3d8b38
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 153 deletions.
20 changes: 13 additions & 7 deletions packages/plugin-depin/src/actions/listPredictions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import {
} from "@elizaos/core";

const formatPrediction = (prediction: any) => {
const deadline = new Date(prediction.deadline).toLocaleDateString();
const deadline = new Date(prediction.deadline).toLocaleString();

return `"${prediction.statement}" (Due: ${deadline})`;
};

export const listPredictions: Action = {
name: "LIST_PREDICTIONS",
similes: ["SHOW_PREDICTIONS", "GET_PREDICTIONS", "VIEW_PREDICTIONS"],
description: "List active predictions and their current status",
validate: async (runtime: IAgentRuntime) => {
description: "List active weather predictions and their current status",
validate: async (_runtime: IAgentRuntime) => {
return !!process.env.BINARY_PREDICTION_CONTRACT_ADDRESS;
},
examples: [
Expand All @@ -40,7 +40,7 @@ export const listPredictions: Action = {
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_state: State,
_options: { [key: string]: unknown },
callback?: HandlerCallback
): Promise<boolean> => {
Expand All @@ -61,14 +61,20 @@ export const listPredictions: Action = {

// Format predictions into a numbered list
const formattedPredictions = predictions
.map((pred, index) => `${index + 1}. ${formatPrediction(pred)}`)
.map((pred) => `ID: ${pred.smartcontract_id}. ${formatPrediction(pred)}`)
.join("\n");

const betText = `You can bet on them by saying "BET ON PREDICTION <number>, <amount> $SENTAI, <outcome>, <your_wallet_address>"`;
const betText = `
You can bet on them by saying
"PREPARE A BET FOR PREDICTION <number>, <amount> $SENTAI, <outcome>, <your_wallet_address>"
Example:
"PREPARE A BET FOR PREDICTION 1, 100 $SENTAI, true, 0x732d35Cc6634C0532915a3b844Bc454e4438f43e"
`;

if (callback) {
callback({
text: `Here are the active predictions:\n${formattedPredictions}\n\n${betText}`,
text: `🎯 Here are the active predictions:\n${formattedPredictions}\n\n${betText}`,
inReplyTo: message.id,
});
}
Expand Down
46 changes: 29 additions & 17 deletions packages/plugin-depin/src/actions/placeBet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const placeBet: Action = {
{
user: "user",
content: {
text: "Here's my approval tx: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
text: "BET 123 APPROVED: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
},
},
{
Expand Down Expand Up @@ -63,8 +63,11 @@ export const placeBet: Action = {
return false;
}

// Verify prediction is still open
const betParams = await extractBetParamsFromContext(runtime, state);
const betParams = await extractBetParamsFromContext(
runtime,
state,
message.content.text
);
if (!betParams) {
if (callback) {
callback({
Expand All @@ -88,7 +91,7 @@ export const placeBet: Action = {
}

// Place the bet on the blockchain
const betTxHash = await executeBlockchainBet(
const betResult = await executeBlockchainBet(
runtime,
betParams.predictionId,
betParams.outcome,
Expand All @@ -99,7 +102,7 @@ export const placeBet: Action = {

if (callback) {
callback({
text: `Your bet has been placed successfully!\n\nPrediction: "${betParams.statement}"\nYour bet: ${betParams.outcome ? "Yes" : "No"}\nAmount: ${betParams.amount} $SENTAI\nTransaction: ${betTxHash}`,
text: `Your bet has been placed successfully!\n\nPrediction: "${betParams.statement}"\nYour bet: ${betParams.outcome ? "Yes" : "No"}\nAmount: ${betResult.betAmount}\nBettor: ${betResult.bettor}\nTransaction: ${betResult.hash}`,
inReplyTo: message.id,
});
}
Expand Down Expand Up @@ -143,13 +146,14 @@ interface BetParams {

async function extractBetParamsFromContext(
runtime: IAgentRuntime,
state: State
state: State,
message: string
): Promise<BetParams> {
const predictions = await runtime.databaseAdapter.getPredictions({
status: "OPEN",
});
state.currentPredictions = predictions;

state.existingPredictions = predictions;
state.userMessage = message;
const context = composeContext({
state,
template: placeBetTemplate,
Expand All @@ -167,16 +171,20 @@ async function extractBetParamsFromContext(
}

const placeBetTemplate = `
Extract address, amount, outcome from the context and relate it to one of the predictions:
Extract bettor address, bet amount, outcome from approved bets and relate it to one of the predictions:
<current_predictions>
{{currentPredictions}}
</current_predictions>
<existing_predictions>
{{existingPredictions}}
</existing_predictions>
<recent_messages>
{{recentMessages}}
</recent_messages>
<user_message>
{{userMessage}}
</user_message>
<example>
<current_prediction_example>
1. Prediction: It will rain tomorrow in London
Expand All @@ -200,19 +208,23 @@ Deadline: Fri Jan 31 2025 23:59:59
3. Prediction: Ethereum gas fees will be under 20 Gwei tomorrow at noon
Deadline: Sat Jan 18 2025 12:00:00
</previous_context_with_predictions>
- BET ON PREDICTION 2, 100 $SENTAI, true, 0x742d35Cc6634C0532925a3b844Bc454e4438f44e
<recent_messages_example>
- BET ON PREDICTION 1, 100 $SENTAI, true, 0x742d35Cc6634C0532925a3b844Bc454e4438f44e
- BetID: 123, 1. 0x742d35Cc6634C0532925a3b844Bc454e4438f44e, 100 $SENTAI, true. Tx data: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
- BET 123 APPROVED: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
</recent_messages_example>
<response>
{
"reasoning": "The user is betting that bitcoin price will exceed $50k by the end of the month with 100 $SENTAI.",
"statement": "Bitcoin price will exceed $50k by the end of the month",
"reasoning": "The user is betting that it will rain tomorrow in London with 100 $SENTAI and he approved the bet.",
"statement": "It will rain tomorrow in London",
"bettor": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"amount": "100",
"outcome": true,
"predictionId": 3
"predictionId": 1
}
</response>
</example>
Now extract the bettor, amount, outcome, and predictionId from the context and return it <response> tags.
Now extract the bettor, amount, outcome, and predictionId from the context and return it in the <response> tags.
`;
103 changes: 66 additions & 37 deletions packages/plugin-depin/src/actions/prepareBet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
State,
HandlerCallback,
elizaLogger,
composeContext,
generateText,
ModelClass,
parseTagContent,
Expand All @@ -15,13 +14,16 @@ import { genTxDataForAllowance } from "../helpers/blockchain";

interface ApprovalParams {
amount: number;
walletAddress: `0x${string}`;
address: `0x${string}`;
outcome: boolean;
predictionId: number;
txData?: `0x${string}`;
}

export const prepareBet: Action = {
name: "PREPARE_BET",
similes: ["SETUP_BET", "START_BET", "INITIALIZE_BET"],
description: "Prepare a bet by generating token approval transaction",
name: "APPROVE_BET",
similes: ["SETUP_BET", "INITIALIZE_BET", "PREPARE_BET"],
description: "Approve and prepare a bet before placing it",
validate: async (_runtime: IAgentRuntime) => {
return !!process.env.BINARY_PREDICTION_CONTRACT_ADDRESS;
},
Expand All @@ -30,13 +32,13 @@ export const prepareBet: Action = {
{
user: "user",
content: {
text: "BET ON PREDICTION 1, 100 $SENTAI, true, 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
text: "PREPARE A BET FOR PREDICTION 1, 100 $SENTAI, true, 0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
},
},
{
user: "assistant",
content: {
text: "Let me prepare your bet. I will generate a QR code for you to approve the tokens first:",
text: "Confirm the following bets:",
action: "PREPARE_BET",
},
},
Expand All @@ -45,18 +47,19 @@ export const prepareBet: Action = {
handler: async (
runtime: IAgentRuntime,
message: Memory,
state: State,
_state: State,
_options: { [key: string]: unknown },
callback?: HandlerCallback
): Promise<boolean> => {
state = (await runtime.composeState(message)) as State;

try {
const params = await extractBetParamsFromContext(runtime, state);
if (!params) {
const betParams = await extractBetParamsFromContext(
runtime,
message.content.text
);
if (!betParams) {
if (callback) {
callback({
text: "Invalid bet format. Please use: BET ON PREDICTION <number>, <amount> $SENTAI, <outcome>, <your_wallet_address>",
text: "Valid bet not found, please try again.",
inReplyTo: message.id,
});
}
Expand All @@ -68,12 +71,17 @@ export const prepareBet: Action = {
throw new Error("Invalid network");
}

// Generate approval transaction data
const txData = await genTxDataForAllowance(runtime, params.amount);
const betWithTx = {
txData: genTxDataForAllowance(betParams.amount),
address: betParams.address,
amount: betParams.amount,
predictionId: betParams.predictionId,
outcome: betParams.outcome,
};

if (callback) {
callback({
text: prepareBetResponse(txData),
text: prepareBetResponse(betWithTx),
inReplyTo: message.id,
});
}
Expand All @@ -94,45 +102,66 @@ export const prepareBet: Action = {

async function extractBetParamsFromContext(
runtime: IAgentRuntime,
state: State
): Promise<ApprovalParams> {
const context = composeContext({
state,
template: prepareBetTemplate,
});

message: string
): Promise<ApprovalParams | null> {
const approvalResponse = await generateText({
runtime,
context,
context: prepareBetTemplate(message),
modelClass: ModelClass.SMALL,
});
const withoutTags = parseTagContent(approvalResponse, "response");

return JSON.parse(withoutTags);
const parsed = JSON.parse(withoutTags);
if (
parsed.address &&
parsed.amount &&
parsed.outcome !== undefined &&
parsed.predictionId
) {
return parsed;
}
return null;
}

const prepareBetResponse = (txData: string) =>
const prepareBetResponse = (approvalTxData: ApprovalParams) =>
`
Please make a transfer with your wallet to ${process.env.SENTAI_ERC20} with the following data: ${txData}.
After approval, send the tx hash like this: "APPROVAL HASH <tx_hash> for PREDICTION <prediction_id>"
`;
🎲 Confirm your bet:
BetID: ${Math.floor(Math.random() * 900) + 100},
PredictionId: ${approvalTxData.predictionId},
Bettor: ${approvalTxData.address},
Amount: ${approvalTxData.amount} $SENTAI,
Outcome: ${approvalTxData.outcome}
Please make a transfer with your wallet to $SENTAI contract:
💰 ${process.env.SENTAI_ERC20}
const prepareBetTemplate = `
Extract address and amount from the context:
With the hex data:
🔐 ${approvalTxData.txData}
<recent_messages>
{{recentMessages}}
</recent_messages>
After approval, send the tx hash like this: "BET <bet_id> APPROVED: <tx_hash>"
Example:
"BET 123 APPROVED: 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
`;

const prepareBetTemplate = (message: string) => `
Extract bet amount and bettor address from the message.
<example>
- BET ON PREDICTION 1, 100 $SENTAI, true, 0x742d35Cc6634C0532925a3b844Bc454e4438f44e
BET ON PREDICTION 1, 100 $SENTAI, true, 0x742d35Cc6634C0532925a3b844Bc454e4438f44e
<response>
{
"predictionId": 1,
"address": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"amount": 100
"amount": 100,
"outcome": true
}
</response>
</example>
Return the JSON object in the <response> tag.
<user_message>
${message}
</user_message>
Return a valid JSON object in the <response> tag.
`;
28 changes: 2 additions & 26 deletions packages/plugin-depin/src/evaluators/predictions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const predictionEvaluator: Evaluator = {
return !!process.env.BINARY_PREDICTION_CONTRACT_ADDRESS;
},
description:
"Extract predictions and forecasts about future events from the conversation, including any associated stakes or bets.",
"Extract weather predictions from the conversation, including any associated stakes or bets.",
handler,
examples: [
{
Expand All @@ -81,30 +81,6 @@ export const predictionEvaluator: Evaluator = {
"statement": "It will rain next Tuesday",
"deadline": "2024-03-26",
}
</response>`,
},
{
context: `Discussion about cryptocurrency prices`,
messages: [
{
user: "{{user1}}",
content: {
text: "Bitcoin will definitely hit 100k by the end of 2024",
},
},
{
user: "{{user2}}",
content: {
text: "I'm willing to stake $50 that you're wrong",
},
},
] as ActionExample[],
outcome: `
<response>
{
"statement": "Bitcoin will reach $100,000",
"deadline": "2024-12-31"
}
</response>`,
},
],
Expand Down Expand Up @@ -140,7 +116,7 @@ async function evaluatePredictionFromContext(
state = (await runtime.composeState(message)) as State;
const formattedPredictions = formatPredictionsAsString({ predictions });

state.currentPredictions = formattedPredictions;
state.existingPredictions = formattedPredictions;
state.currentTime = new Date().toISOString();

const context = composeContext({
Expand Down
Loading

0 comments on commit e3d8b38

Please sign in to comment.