From eceb46b0e3cc0cc4547bef0d99b54768eeb56129 Mon Sep 17 00:00:00 2001 From: astinz <28899947+astinz@users.noreply.github.com> Date: Mon, 23 Dec 2024 00:30:26 +0300 Subject: [PATCH 1/2] feat: ptb generation action --- packages/plugin-sui/src/actions/ptb.ts | 354 +++++++++++++++++++++++++ packages/plugin-sui/src/index.ts | 3 +- pnpm-lock.yaml | 20 +- 3 files changed, 366 insertions(+), 11 deletions(-) create mode 100644 packages/plugin-sui/src/actions/ptb.ts diff --git a/packages/plugin-sui/src/actions/ptb.ts b/packages/plugin-sui/src/actions/ptb.ts new file mode 100644 index 0000000000..661acada66 --- /dev/null +++ b/packages/plugin-sui/src/actions/ptb.ts @@ -0,0 +1,354 @@ +import { z } from "zod"; +import { + Action, + ActionExample, + composeContext, + Content, + elizaLogger, + generateObject, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, +} from "@ai16z/eliza"; + +const ArgSchema = z.union([ + z.object({ + kind: z.literal("Input"), + index: z.number(), + }), + z.object({ + kind: z.literal("Result"), + index: z.number(), + }), +]); + +const InputArgSchema = z.object({ + kind: z.literal("Input"), + index: z.number(), +}); + +const TransferObjectsSchema = z.object({ + TransferObjects: z.object({ + objects: z.array(ArgSchema), + address: ArgSchema, + }), +}); + +const SplitCoinsSchema = z.object({ + SplitCoins: z.object({ + coin: ArgSchema, + amounts: z.array(ArgSchema), + }), +}); + +const MergeCoinsSchema = z.object({ + MergeCoins: z.object({ + coin: ArgSchema, + toMerge: z.array(ArgSchema), + }), +}); + +const MakeMoveVecSchema = z.object({ + MakeMoveVec: z.object({ + args: z.array(InputArgSchema), + }), +}); + +const MoveCallSchema = z.object({ + MoveCall: z.object({ + target: InputArgSchema, + typeArgs: z.array(InputArgSchema), + args: z.array(ArgSchema), + }), +}); + +const PublishSchema = z.object({ + Publish: z.object({ + moduleBytes: z.array(InputArgSchema), + transitiveDependencies: z.array(InputArgSchema), + }), +}); + +const UpgradeSchema = z.object({ + Upgrade: z.object({ + moduleBytes: z.array(InputArgSchema), + transitiveDependencies: z.array(InputArgSchema), + package: InputArgSchema, + upgradeTicket: InputArgSchema, + }), +}); + +const CommandSchema = z.union([ + TransferObjectsSchema, + SplitCoinsSchema, + MergeCoinsSchema, + MakeMoveVecSchema, + MoveCallSchema, + PublishSchema, + UpgradeSchema, +]); + +const ptbSchema = z.object({ + inputs: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])), + commands: z.array(CommandSchema), +}); + +export interface PTBContent extends Content { + inputs: (string | number | boolean | null)[]; + commands: object[]; +} + +function isPTBContent(content: Content): content is PTBContent { + return ( + content && + typeof content === "object" && + Array.isArray(content.inputs) && + content.inputs.every((input) => + ["string", "number", "boolean"].includes(typeof input) + ) && + Array.isArray(content.commands) && + content.commands.every((command) => typeof command === "object") + ); +} + +const ptbTemplate = `Respond with a JSON markdown block containing only the extracted commands and inputs. Use null for any commands and inputs that cannot be determined. +TransferObjects sends multiple (one or more) objects to a specified address. +SplitCoins splits off multiple (one or more) coins from a single coin. +MergeCoins merges multiple (one or more) coins into a single coin. +MakeMoveVec creates a vector (potentially empty) of Move values. +MoveCall invokes either an entry or a public Move function in a published package. +Publish creates a new package and calls the init function of each module in the package. +Upgrade upgrades an existing package. + +Example responses: +\`\`\`json +{ + "inputs": ["0x1", 10, 20], + "commands": [ + { + "SplitCoins": { + "coin": { "kind": "Input", "index": 0 }, + "amounts": [ + { "kind": "Input", "index": 1 }, + { "kind": "Input", "index": 2 } + ] + } + } + ] +} +\`\`\` + +\`\`\`json +{ + "inputs": ["0x1", "0x2", "0x3"], + "commands": [{ + "MakeMoveVec": { + "args": [ + { "kind": "Input", "index": 0 }, + { "kind": "Input", "index": 1 }, + { "kind": "Input", "index": 2 } + ] + } + }] +} +\`\`\` + +\`\`\`json +{ + "inputs": ["0x1", "0x2", "0x3", "0x4"], + "commands": [{ + "TransferObjects": { + "objects": [ + { "kind": "Input", "index": 0 }, + { "kind": "Input", "index": 1 }, + { "kind": "Input", "index": 2 } + ], + "address": { "kind": "Input", "index": 3 } + } + }] +} +\`\`\` + +\`\`\`json +{ + "inputs": ["0x1", "0x2", "0x3", "0x4"], + "commands": [{ + "MergeCoins": { + "coin": { "kind": "Input", "index": 0 }, + "toMerge": [ + { "kind": "Input", "index": 1 }, + { "kind": "Input", "index": 2 }, + { "kind": "Input", "index": 3 } + ] + } + }] +} +\`\`\` + +\`\`\`json +{ + "inputs": ["0x1", "0x2", "0x3", "0x4"], + "commands": [{ + "MoveCall": { + "target": { "kind": "Input", "index": 0 }, + "typeArgs": [], + "args": [ + { "kind": "Input", "index": 1 }, + { "kind": "Input", "index": 2 }, + { "kind": "Input", "index": 3 } + ] + } + }] +} +\`\`\` + +\`\`\`json +{ + "inputs": ["0x1", "0x2", "0x3", "0x4", "0x5", "0x6"], + "commands": [ + { + "Publish": { + "moduleBytes": [ + { "kind": "Input", "index": 0 }, + { "kind": "Input", "index": 1 }, + { "kind": "Input", "index": 2 } + ], + "transitiveDependencies": [ + { "kind": "Input", "index": 3 }, + { "kind": "Input", "index": 4 }, + { "kind": "Input", "index": 5 } + ] + } + } + ] +} +\`\`\` + +{{recentMessages}} + +Respond with a JSON markdown block containing only the extracted commands and inputs.`; + +export default { + name: "BUILD_PTB", + similes: [ + "CONSTRUCT_PTB", + "COMPOSE_PTB", + "GENERATE_PTB", + "PTB", + "COMMAND", + "TRANSACTION", + ], + description: "Build a PTB from inputs and commands", + validate: async (runtime: IAgentRuntime, message: Memory) => { + console.log("Validating PTB build from user:", message.userId); + return true; + }, + handler: async ( + runtime: IAgentRuntime, + message: Memory, + state: State, + _options: { [key: string]: unknown }, + callback?: HandlerCallback + ): Promise => { + elizaLogger.log("Starting BUILD_PTB handler..."); + + if (!state) { + state = (await runtime.composeState(message)) as State; + } else { + state = await runtime.updateRecentMessageState(state); + } + + const ptbContext = composeContext({ + state, + template: ptbTemplate, + }); + + const content = await generateObject({ + runtime, + context: ptbContext, + schema: ptbSchema, + modelClass: ModelClass.SMALL, + }); + + const ptbContent = content.object as PTBContent; + + if (!isPTBContent(ptbContent)) { + console.error("Invalid PTB content:", ptbContent); + if (callback) { + await callback({ + text: "Unable to process PTB request. Invalid content provided.", + content: { error: "Invalid PTB content" }, + }); + } + } + + if (callback) { + await callback({ + text: JSON.stringify(ptbContent), + content: { + success: true, + }, + }); + } + + return true; + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Transfer objects 0x1, 0x2, and 0x3 to address 0x4", + }, + }, + { + user: "{{user2}}", + content: { + text: "I will generate a PTB that transfers the objects 0x1, 0x2, 0x3 to the address 0x4", + action: "BUILD_PTB", + }, + }, + { + user: "{{user1}}", + content: { + text: "Split coin 0x1 into amounts 10 and 20", + }, + }, + { + user: "{{user2}}", + content: { + text: "I will generate a PTB that splits the coin 0x1 into amounts 10 and 20", + action: "BUILD_PTB", + }, + }, + { + user: "{{user1}}", + content: { + text: "Merge the coins 0x1, 0x2, and 0x3 into a single coin", + }, + }, + { + user: "{{user2}}", + content: { + text: "I will generate a PTB that merges the input coins 0x1, 0x2, into 0x3 coin", + action: "BUILD_PTB", + }, + }, + { + user: "{{user1}}", + content: { + text: "Construct a PTB that calls the target 0x883393ee444fb828aa0e977670cf233b0078b41d144e6208719557cb3888244d::hello_wolrd::hello_world with the argument 50", + }, + }, + { + user: "{{user2}}", + content: { + text: "I will generate a PTB that calls the input target 0x883393ee444fb828aa0e977670cf233b0078b41d144e6208719557cb3888244d::hello_wolrd::hello_world with the input argument 50", + action: "BUILD_PTB", + }, + }, + ], + ] as ActionExample[][], +} as Action; diff --git a/packages/plugin-sui/src/index.ts b/packages/plugin-sui/src/index.ts index 5f69381fda..3c0ab5eee2 100644 --- a/packages/plugin-sui/src/index.ts +++ b/packages/plugin-sui/src/index.ts @@ -1,5 +1,6 @@ import { Plugin } from "@elizaos/core"; import transferToken from "./actions/transfer.ts"; +import buildPTB from "./actions/ptb.ts"; import { WalletProvider, walletProvider } from "./providers/wallet.ts"; export { WalletProvider, transferToken as TransferSuiToken }; @@ -7,7 +8,7 @@ export { WalletProvider, transferToken as TransferSuiToken }; export const suiPlugin: Plugin = { name: "sui", description: "Sui Plugin for Eliza", - actions: [transferToken], + actions: [transferToken, buildPTB], evaluators: [], providers: [walletProvider], }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f0e904aa4..0f456ca6e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -549,13 +549,13 @@ importers: version: link:../plugin-node '@discordjs/opus': specifier: github:discordjs/opus - version: https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13) + version: git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13) '@discordjs/rest': specifier: 2.4.0 version: 2.4.0 '@discordjs/voice': specifier: 0.17.0 - version: 0.17.0(@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(bufferutil@4.0.8)(ffmpeg-static@5.2.0)(utf-8-validate@5.0.10) + version: 0.17.0(@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(bufferutil@4.0.8)(ffmpeg-static@5.2.0)(utf-8-validate@5.0.10) discord.js: specifier: 14.16.3 version: 14.16.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -564,7 +564,7 @@ importers: version: 0.7.15 prism-media: specifier: 1.3.5 - version: 1.3.5(@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(ffmpeg-static@5.2.0) + version: 1.3.5(@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(ffmpeg-static@5.2.0) whatwg-url: specifier: 7.1.0 version: 7.1.0 @@ -3417,8 +3417,8 @@ packages: resolution: {integrity: sha512-YJOVVZ545x24mHzANfYoy0BJX5PDyeZlpiJjDkUBM/V/Ao7TFX9lcUvCN4nr0tbr5ubeaXxtEBILUrHtTphVeQ==} hasBin: true - '@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02': - resolution: {tarball: https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02} + '@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02': + resolution: {commit: 31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02, repo: git@github.com:discordjs/opus.git, type: git} version: 0.9.0 engines: {node: '>=12.0.0'} @@ -21537,7 +21537,7 @@ snapshots: - encoding - supports-color - '@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13)': + '@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13)': dependencies: '@discordjs/node-pre-gyp': 0.4.5(encoding@0.1.13) node-addon-api: 8.3.0 @@ -21559,11 +21559,11 @@ snapshots: '@discordjs/util@1.1.1': {} - '@discordjs/voice@0.17.0(@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(bufferutil@4.0.8)(ffmpeg-static@5.2.0)(utf-8-validate@5.0.10)': + '@discordjs/voice@0.17.0(@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(bufferutil@4.0.8)(ffmpeg-static@5.2.0)(utf-8-validate@5.0.10)': dependencies: '@types/ws': 8.5.13 discord-api-types: 0.37.83 - prism-media: 1.3.5(@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(ffmpeg-static@5.2.0) + prism-media: 1.3.5(@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(ffmpeg-static@5.2.0) tslib: 2.8.1 ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: @@ -38456,9 +38456,9 @@ snapshots: pretty-time@1.1.0: {} - prism-media@1.3.5(@discordjs/opus@https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(ffmpeg-static@5.2.0): + prism-media@1.3.5(@discordjs/opus@git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13))(ffmpeg-static@5.2.0): optionalDependencies: - '@discordjs/opus': https://codeload.github.com/discordjs/opus/tar.gz/31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13) + '@discordjs/opus': git+https://git@github.com:discordjs/opus.git#31da49d8d2cc6c5a2ab1bfd332033ff7d5f9fb02(encoding@0.1.13) ffmpeg-static: 5.2.0 prism-react-renderer@2.3.1(react@18.3.1): From 5f27d1329828d672553c7992772a83394cd666b5 Mon Sep 17 00:00:00 2001 From: astinz <28899947+astinz@users.noreply.github.com> Date: Tue, 24 Dec 2024 08:06:17 +0300 Subject: [PATCH 2/2] minor fix... --- packages/plugin-sui/src/actions/ptb.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-sui/src/actions/ptb.ts b/packages/plugin-sui/src/actions/ptb.ts index 661acada66..968bd22af0 100644 --- a/packages/plugin-sui/src/actions/ptb.ts +++ b/packages/plugin-sui/src/actions/ptb.ts @@ -11,7 +11,7 @@ import { Memory, ModelClass, State, -} from "@ai16z/eliza"; +} from "@elizaos/core"; const ArgSchema = z.union([ z.object({