Skip to content

Commit

Permalink
ELIZAAI-18 Added error handling, improved prompt data extracting, fix…
Browse files Browse the repository at this point in the history
…ed denom fetching
  • Loading branch information
stanislawkurzypBD committed Jan 10, 2025
1 parent f0abcf5 commit 131e783
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 113 deletions.
90 changes: 41 additions & 49 deletions packages/plugin-cosmos/src/actions/ibc-swap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,22 @@ import {
IAgentRuntime,
Memory,
ModelClass,
State
State,
} from "@elizaos/core";

import { initWalletChainsData } from "../../providers/wallet/utils";
import {
cosmosIBCSwapTemplate,
cosmosTransferTemplate,
} from "../../templates";
import { cosmosIBCSwapTemplate, cosmosTransferTemplate } from "../../templates";
import type {
ICosmosPluginOptions,
ICosmosWalletChains,
} from "../../shared/interfaces";
import {IBCSwapActionParams} from "./types.ts";
import {IBCSwapAction} from "./services/ibc-swap-action-service.ts";
import { IBCSwapActionParams } from "./types.ts";
import { IBCSwapAction } from "./services/ibc-swap-action-service.ts";
import { prepareAmbiguityErrorMessage } from "./services/utils.ts";

export const createIBCSwapAction = (
pluginOptions: ICosmosPluginOptions
) => ({
export const createIBCSwapAction = (pluginOptions: ICosmosPluginOptions) => ({
name: "COSMOS_IBC_SWAP",
description: "Swaps tokens between addresses on cosmos chains",
description: "Swaps tokens on cosmos chains",
handler: async (
_runtime: IAgentRuntime,
_message: Memory,
Expand All @@ -50,15 +46,20 @@ export const createIBCSwapAction = (
fromTokenAmount: cosmosIBCSwapContent.fromTokenAmount,
toTokenSymbol: cosmosIBCSwapContent.toTokenSymbol,
toChainName: cosmosIBCSwapContent.toChainName,
toTokenDenom: cosmosIBCSwapContent?.toTokenDenom || undefined,
fromTokenDenom: cosmosIBCSwapContent?.fromTokenDenom || undefined,
};

console.log('ParamOptions: ',JSON.stringify(paramOptions, null, 2));
console.log(
"Parameters extracted from user prompt: ",
JSON.stringify(paramOptions, null, 2)
);

try {
const walletProvider: ICosmosWalletChains =
await initWalletChainsData(_runtime);

const action = new IBCSwapAction(walletProvider)
const action = new IBCSwapAction(walletProvider);

const customAssets = (pluginOptions?.customChainData ?? []).map(
(chainData) => chainData.assets
Expand All @@ -72,52 +73,46 @@ export const createIBCSwapAction = (

if (_callback) {
await _callback({
text: `Successfully swapped ${transferResp.fromTokenAmount} ${transferResp.fromTokenSymbol} tokens to ${transferResp.toTokenAmount} ${transferResp.toTokenSymbol} on chain ${transferResp.toChainName} \nGas paid: ${transferResp.gasPaid}\nTransaction Hash: ${transferResp.txHash}`,
text: `Successfully swapped ${transferResp.fromTokenAmount} ${transferResp.fromTokenSymbol} tokens to ${transferResp.toTokenSymbol} on chain ${transferResp.toChainName}.\nTransaction Hash: ${transferResp.txHash}`,
content: {
success: true,
hash: transferResp.txHash,
fromTokenAmount: paramOptions.fromTokenAmount,
fromToken: paramOptions.fromTokenSymbol,
toTokenAmount: 'not provided yet',
toToken: paramOptions.toTokenSymbol,
fromChain: paramOptions.fromChainName,
toChain: paramOptions.toChainName,
},
});

const newMemory: Memory = {
userId: _message.agentId,
agentId: _message.agentId,
roomId: _message.roomId,
content: {
text: `Swap of ${transferResp.fromTokenAmount} ${transferResp.fromTokenSymbol} to address ${transferResp.toTokenAmount} ${transferResp.toTokenSymbol} on chain ${transferResp.toChainName} was successful.\n Gas paid: ${transferResp.gasPaid}. Tx hash: ${transferResp.txHash}`,
},
};

await _runtime.messageManager.createMemory(newMemory);
}
return true;
} catch (error) {
console.error("Error during ibc token swap:", error);

if (_callback) {
await _callback({
text: `Error ibc swapping tokens: ${error.message}`,
content: { error: error.message },
});
console.error("Error during ibc token transfer:", error);

const regex =
/Ambiguity Error.*value:([^\s.]+)\s+chainName:([^\s.]+)/;
const match = error.message.match(regex);

if (match) {
const value = match[1];
const chainName = match[2];

if (_callback) {
await _callback({
text: prepareAmbiguityErrorMessage(value, chainName),
content: { error: error.message },
});
}
} else {
console.error("Unhandled error:", error);

if (_callback) {
await _callback({
text: `Error ibc transferring tokens: ${error.message}`,
content: { error: error.message },
});
}
}

const newMemory: Memory = {
userId: _message.agentId,
agentId: _message.agentId,
roomId: _message.roomId,
content: {
text: `Swap of ${paramOptions.fromTokenAmount} ${paramOptions.fromTokenSymbol} to ${paramOptions.toTokenSymbol} on chain ${paramOptions.toChainName} was unsuccessful.`,
},
};

await _runtime.messageManager.createMemory(newMemory);

return false;
}
},
Expand Down Expand Up @@ -191,8 +186,5 @@ export const createIBCSwapAction = (
},
],
],
similes: [
"COSMOS_SWAP",
"COSMOS_SWAP_IBC",
],
similes: ["COSMOS_SWAP", "COSMOS_SWAP_IBC"],
});
2 changes: 2 additions & 0 deletions packages/plugin-cosmos/src/actions/ibc-swap/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ export const IBCSwapParamsSchema = z.object({
fromTokenAmount: z.string(),
toTokenSymbol: z.string(),
toChainName: z.string(),
toTokenDenom: z.string(),
fromTokenDenom: z.string(),
});
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {assets, chains } from "chain-registry";
import { assets, chains } from "chain-registry";
import {
ICosmosActionService, ICosmosPluginCustomChainData,
ICosmosActionService,
ICosmosPluginCustomChainData,
ICosmosSwap,
ICosmosWalletChains,
} from "../../../shared/interfaces.ts";
import { IBCSwapActionParams } from "../types.ts";
import {
getAssetBySymbol,
getChainByChainName,
getChainNameByChainId
getChainNameByChainId,
getDenomBySymbol,
getExponentByDenom,
} from "@chain-registry/utils";
import { getAvailableAssets } from "../../../shared/helpers/cosmos-assets.ts";
import {HandlerCallback} from "@elizaos/core";
import { HandlerCallback } from "@elizaos/core";
import { calculateAmountInDenomFromDisplayUnit } from "./utils.ts";

export class IBCSwapAction implements ICosmosActionService {
constructor(private cosmosWalletChains: ICosmosWalletChains) {
Expand All @@ -26,60 +29,52 @@ export class IBCSwapAction implements ICosmosActionService {
const fromChain = getChainByChainName(chains, params.fromChainName);
const toChain = getChainByChainName(chains, params.toChainName);

const availableAssets = getAvailableAssets(assets, customChainAssets)
const availableAssets = getAvailableAssets(assets, customChainAssets);

const denomFrom = getAssetBySymbol(
availableAssets,
params.fromTokenSymbol,
params.fromChainName
);
const denomFrom =
params.fromTokenDenom ||
getDenomBySymbol(
availableAssets,
params.fromTokenSymbol,
params.fromChainName
);

const denomTo = getAssetBySymbol(
const exponentFrom = getExponentByDenom(
availableAssets,
params.toTokenSymbol,
params.toChainName
denomFrom,
params.fromChainName
);

console.log('denomFrom: ',JSON.stringify(denomFrom.base, null, 2));
console.log('denomTo: ',JSON.stringify(denomTo.base, null, 2));

if( !denomFrom ) {
//TODO: use skip endpoint
}

if( !denomTo ) {
//TODO: use skip endpoint
}

console.log('denomFrom: ',JSON.stringify(denomFrom.base, null, 2));
console.log('denomTo: ',JSON.stringify(denomTo.base, null, 2));

const denomTo =
params.toTokenDenom ||
getDenomBySymbol(
availableAssets,
params.toTokenSymbol,
params.toChainName
);

console.log(
`Swap data: Swapping token ${denomFrom} with exponent ${exponentFrom} to token ${denomTo}`
);

const skipClient = this.cosmosWalletChains.getSkipClient(
params.fromChainName
);


const route = await skipClient.route({
smartSwapOptions: {},
amountIn: params.fromTokenAmount,
sourceAssetDenom: denomFrom.base,
amountOut: calculateAmountInDenomFromDisplayUnit(
params.fromTokenAmount,
exponentFrom
),
sourceAssetDenom: denomFrom,
sourceAssetChainID: fromChain.chain_id,
destAssetDenom: denomTo.base,
destAssetDenom: denomTo,
destAssetChainID: toChain.chain_id,
});

// const route = await skipClient.route({
// amountIn: params.fromTokenAmount,
// sourceAssetDenom: "uosmo",
// sourceAssetChainID: "osmo-test-5",
// destAssetDenom: "ibc/6D3D88AFE4BFF8F478277916EFEB6CD4507E1E791CB7847A9F42264BA236B6F7",
// destAssetChainID: "pion-1",
// cumulativeAffiliateFeeBPS: "0",
// });

// TODO: remember to add chain to available chains in .env !!
// Required chains must be added to env file. Note that swaps can use intermediate chains to complete the swap request
// These chains should also be included
const userAddresses = await Promise.all(
route.requiredChainAddresses.map(async (chainID) => {
const chainName = getChainNameByChainId(chains, chainID);
Expand All @@ -93,19 +88,6 @@ export class IBCSwapAction implements ICosmosActionService {
})
);

// console.log('addresses: ',JSON.stringify(userAddresses, null, 2));

// const userAddresses: UserAddress[] = [
// {
// chainID: "osmo-test-5",
// address: "osmo16cnsf5txawpde3wgycz83ukxlthsucdwhkgztv",
// },
// {
// chainID: "pion-1",
// address: "neutron16cnsf5txawpde3wgycz83ukxlthsucdwmjjs8e",
// },
// ];

if (_callback) {
await _callback({
text: `Expected swap result: ${route.estimatedAmountOut} ${params.toTokenSymbol}, \nEstimated Fee: ${route.estimatedFees}. \nEstimated time: ${route.estimatedRouteDurationSeconds}`,
Expand All @@ -122,16 +104,11 @@ export class IBCSwapAction implements ICosmosActionService {
`Route completed with tx hash: ${txHash} & status: ${status.state}`
);

//if state != STATE_COMPLETED || STATE_COMPLETED_SUCCESS
//throw error ??

result = {
fromChainName: params.fromChainName,
fromTokenAmount: params.fromTokenAmount,
fromTokenSymbol: params.fromTokenSymbol,
gasPaid: 0,
toChainName: params.toChainName,
toTokenAmount: "ile??", //todo: get exchange result
toTokenSymbol: params.toTokenSymbol,
txHash,
};
Expand Down
35 changes: 35 additions & 0 deletions packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { assets } from "chain-registry";
import BigNumber from "bignumber.js";

export const prepareAmbiguityErrorMessage = (
coinSymbol: string,
chainName: string
): string => {
const chainAssets = assets.find((chain) => chain.chain_name === chainName);

const ambiguousAssets = chainAssets.assets.filter(
(asset) => asset.symbol === coinSymbol
);

console.log(
`Ambiguous Assets found: ${JSON.stringify(ambiguousAssets, null, 2)}`
);

const assetsText = `${ambiguousAssets.map((a) => `Symbol: ${a.symbol} Desc: ${a.description} Denom: ${a.base}`).join(",\n")}`;

return `Error occured. Swap was not performed. Please provide denom for coin: ${coinSymbol}, on Chain Name: ${chainName}. It is necessary as the symbol ${coinSymbol} is not unique among coins on chain ${chainName}. \n Select one from found assets:\n${assetsText}`;
};

/**
* Calculates amount passed in display unit
* @param tokenAmount
* @param exponet
*/
export const calculateAmountInDenomFromDisplayUnit = (
tokenAmount: string,
exponet: number
) => {
return new BigNumber(tokenAmount)
.multipliedBy(new BigNumber(10).pow(exponet))
.toString();
};
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class CosmosWalletChains implements ICosmosWalletChains {
public async getWalletAddress(chainName: string) {
const chainWalletsForGivenChain = this.walletChainsData[chainName];
if (!chainWalletsForGivenChain) {
throw new Error("Invalid chain name");
throw new Error(`Invalid chain name. If ${chainName} is required, it should be added to env file.`);
}

return await chainWalletsForGivenChain.wallet.getWalletAddress();
Expand Down
2 changes: 0 additions & 2 deletions packages/plugin-cosmos/src/shared/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ export interface ICosmosSwap {
fromTokenSymbol: string;
fromTokenAmount: string;
toTokenSymbol: string;
toTokenAmount: string;
toChainName: string;
txHash: string;
gasPaid: number;
}

export interface ICosmosWallet {
Expand Down
Loading

0 comments on commit 131e783

Please sign in to comment.