From a9fecd37e07a24fd1cf4dfed32943240864eb5cf Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Wed, 8 Jan 2025 13:47:25 +0100 Subject: [PATCH 01/12] feat: ibc transfer action --- packages/plugin-cosmos/package.json | 1 + .../src/actions/ibc-transfer/index.ts | 226 +++++++++++++ .../src/actions/ibc-transfer/schema.ts | 37 +++ .../services/bridge-data-fetcher.ts | 68 ++++ .../services/bridge-data-provider.ts | 33 ++ .../services/ibc-transfer-action-service.ts | 129 ++++++++ .../src/actions/ibc-transfer/types.ts | 18 + .../src/shared/helpers/cosmos-messages.ts | 11 + .../plugin-cosmos/src/shared/interfaces.ts | 7 + .../cosmos-transaction-fee-estimator.ts | 14 + packages/plugin-cosmos/src/templates/index.ts | 47 +++ .../src/tests/bridge-data-fetcher.test.ts | 101 ++++++ .../src/tests/bridge-data-provider.test.ts | 103 ++++++ ...cosmos-ibc-transfer-action-service.test.ts | 309 ++++++++++++++++++ .../src/tests/cosmos-message.test.ts | 33 ++ .../cosmos-transaction-fee-estimator.test.ts | 102 ++++++ pnpm-lock.yaml | 99 +++++- 17 files changed, 1337 insertions(+), 1 deletion(-) create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/index.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/types.ts create mode 100644 packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts create mode 100644 packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts create mode 100644 packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts create mode 100644 packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts create mode 100644 packages/plugin-cosmos/src/tests/cosmos-message.test.ts diff --git a/packages/plugin-cosmos/package.json b/packages/plugin-cosmos/package.json index 80512fa9696..b052b3060ed 100644 --- a/packages/plugin-cosmos/package.json +++ b/packages/plugin-cosmos/package.json @@ -10,6 +10,7 @@ "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/proto-signing": "^0.32.4", "@cosmjs/stargate": "^0.32.4", + "axios": "^1.7.9", "bignumber.js": "9.1.2", "chain-registry": "^1.69.68", "tsup": "8.3.5", diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts new file mode 100644 index 00000000000..f0833c3983b --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -0,0 +1,226 @@ +import { + composeContext, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State, +} from "@ai16z/eliza"; +import { initWalletChainsData } from "../../providers/wallet/utils"; +import { + cosmosIBCTransferTemplate, + cosmosTransferTemplate, +} from "../../templates"; +import type { + ICosmosPluginOptions, + ICosmosWalletChains, +} from "../../shared/interfaces"; +import { IBCTransferActionParams } from "./types"; +import { CosmosIBCTransferAction } from "./services/ibc-transfer-action-service"; +import { bridgeDataProvider } from "./services/bridge-data-provider"; + +export const createIBCTransferAction = ( + pluginOptions: ICosmosPluginOptions +) => ({ + name: "COSMOS_IBC_TRANSFER", + description: "Transfer tokens between addresses on cosmos chains", + handler: async ( + _runtime: IAgentRuntime, + _message: Memory, + state: State, + _options: { [key: string]: unknown }, + _callback?: HandlerCallback + ) => { + const cosmosIBCTransferContext = composeContext({ + state: state, + template: cosmosIBCTransferTemplate, + templatingEngine: "handlebars", + }); + + const cosmosIBCTransferContent = await generateObjectDeprecated({ + runtime: _runtime, + context: cosmosIBCTransferContext, + modelClass: ModelClass.SMALL, + }); + + const paramOptions: IBCTransferActionParams = { + chainName: cosmosIBCTransferContent.chainName, + symbol: cosmosIBCTransferContent.symbol, + amount: cosmosIBCTransferContent.amount, + toAddress: cosmosIBCTransferContent.toAddress, + targetChainName: cosmosIBCTransferContent.targetChainName, + }; + + try { + const walletProvider: ICosmosWalletChains = + await initWalletChainsData(_runtime); + + const action = new CosmosIBCTransferAction(walletProvider); + + const customAssets = (pluginOptions?.customChainData ?? []).map( + (chainData) => chainData.assets + ); + + const transferResp = await action.execute( + paramOptions, + bridgeDataProvider, + customAssets + ); + + if (_callback) { + await _callback({ + text: `Successfully transferred ${paramOptions.amount} tokens from ${paramOptions.chainName} to ${paramOptions.toAddress} on ${paramOptions.targetChainName}\nGas paid: ${transferResp.gasPaid}\nTransaction Hash: ${transferResp.txHash}`, + content: { + success: true, + hash: transferResp.txHash, + amount: paramOptions.amount, + recipient: transferResp.to, + fromChain: paramOptions.chainName, + toChain: paramOptions.targetChainName, + }, + }); + + const newMemory: Memory = { + userId: _message.agentId, + agentId: _message.agentId, + roomId: _message.roomId, + content: { + text: `Transaction ${paramOptions.amount} ${paramOptions.symbol} to address ${paramOptions.toAddress} from chain ${paramOptions.chainName} to ${paramOptions.targetChainName} was successfully transferred.\n Gas paid: ${transferResp.gasPaid}. Tx hash: ${transferResp.txHash}`, + }, + }; + + await _runtime.messageManager.createMemory(newMemory); + } + return true; + } catch (error) { + console.error("Error during ibc token transfer:", 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: `Transaction ${paramOptions.amount} ${paramOptions.symbol} to address ${paramOptions.toAddress} on chain ${paramOptions.chainName} to ${paramOptions.targetChainName} was unsuccessful.`, + }, + }; + + await _runtime.messageManager.createMemory(newMemory); + + return false; + } + }, + template: cosmosTransferTemplate, + validate: async (runtime: IAgentRuntime) => { + const mnemonic = runtime.getSetting("COSMOS_RECOVERY_PHRASE"); + const availableChains = runtime.getSetting("COSMOS_AVAILABLE_CHAINS"); + const availableChainsArray = availableChains?.split(","); + + return !(mnemonic && availableChains && availableChainsArray.length); + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Make an IBC transfer {{0.0001 ATOM}} to {{osmosis1pcnw46km8m5amvf7jlk2ks5std75k73aralhcf}} from {{cosmoshub}} to {{osmosis}}", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user2}}", + content: { + text: "Do you confirm the IBC transfer action?", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user1}}", + content: { + text: "Yes", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user2}}", + content: { + text: "", + action: "COSMOS_IBC_TRANSFER", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Send {{50 OSMO}} to {{juno13248w8dtnn07sxc3gq4l3ts4rvfyat6f4qkdd6}} from {{osmosis}} to {{juno}}", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user2}}", + content: { + text: "Do you confirm the IBC transfer action?", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user1}}", + content: { + text: "Yes", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user2}}", + content: { + text: "", + action: "COSMOS_IBC_TRANSFER", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Transfer {{0.005 JUNO}} from {{juno}} to {{cosmos1n0xv7z2pkl4eppnm7g2rqhe2q8q6v69h7w93fc}} on {{cosmoshub}}", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user2}}", + content: { + text: "Do you confirm the IBC transfer action?", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user1}}", + content: { + text: "Yes", + action: "COSMOS_IBC_TRANSFER", + }, + }, + { + user: "{{user2}}", + content: { + text: "", + action: "COSMOS_IBC_TRANSFER", + }, + }, + ], + ], + similes: [ + "COSMOS_TRANSFER", + "COSMOS_SEND_TOKENS", + "COSMOS_TOKEN_TRANSFER", + "COSMOS_MOVE_TOKENS", + ], +}); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts new file mode 100644 index 00000000000..018e892d242 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts @@ -0,0 +1,37 @@ +import { z } from "zod"; + +export const IBCTransferParamsSchema = z.object({ + chainName: z.string(), + symbol: z.string(), + amount: z.string(), + toAddress: z.string(), + targetChainName: z.string(), +}); + +export const bridgeDataProviderParamsSchema = z.object({ + source_asset_denom: z.string(), + source_asset_chain_id: z.string(), + allow_multi_tx: z.boolean(), +}); + +export const bridgeDataProviderResponseAssetsSchema = z.object({ + denom: z.string(), + chain_id: z.string(), + origin_denom: z.string(), + origin_chain_id: z.string(), + trace: z.string(), + symbol: z.string().optional(), + name: z.string().optional(), + logo_uri: z.string().optional(), + decimals: z.number().optional(), + recommended_symbol: z.string().optional(), +}); + +export const bridgeDataProviderResponseSchema = z.object({ + dest_assets: z.record( + z.string(), + z.object({ + assets: z.array(bridgeDataProviderResponseAssetsSchema), + }) + ), +}); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts new file mode 100644 index 00000000000..1ababb35214 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts @@ -0,0 +1,68 @@ +import { bridgeDataProviderResponseSchema } from "../schema"; +import { BridgeDataProviderParams, BridgeDataProviderResponse } from "../types"; +import axios from "axios"; + +type CacheKey = `${string}_${string}`; + +export class BridgeDataFetcher { + private static instance: BridgeDataFetcher; + private cache: Map; + private readonly apiUrl: string; + + private constructor() { + this.cache = new Map(); + this.apiUrl = "https://api.skip.build/v2/fungible/assets_from_source"; + } + + public static getInstance(): BridgeDataFetcher { + if (!BridgeDataFetcher.instance) { + BridgeDataFetcher.instance = new BridgeDataFetcher(); + } + return BridgeDataFetcher.instance; + } + + private generateCacheKey( + sourceAssetDenom: string, + sourceAssetChainId: string + ): CacheKey { + return `${sourceAssetDenom}_${sourceAssetChainId}`; + } + + public async fetchBridgeData( + sourceAssetDenom: string, + sourceAssetChainId: string + ): Promise { + const cacheKey = this.generateCacheKey( + sourceAssetDenom, + sourceAssetChainId + ); + + if (this.cache.has(cacheKey)) { + return this.cache.get(cacheKey)!; + } + + const requestData: BridgeDataProviderParams = { + source_asset_denom: sourceAssetDenom, + source_asset_chain_id: sourceAssetChainId, + allow_multi_tx: false, + }; + + try { + const response = await axios.post(this.apiUrl, requestData, { + headers: { + "Content-Type": "application/json", + }, + }); + + const validResponse = bridgeDataProviderResponseSchema.parse( + response.data + ); + + this.cache.set(cacheKey, validResponse); + return response.data; + } catch (error) { + console.error("Error fetching assets:", error); + throw error; + } + } +} diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts new file mode 100644 index 00000000000..b20cc68124e --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts @@ -0,0 +1,33 @@ +import { IBridgeDataProvider } from "../../../shared/interfaces"; +import { BridgeDataFetcher } from "./bridge-data-fetcher"; + +export const bridgeDataProvider: IBridgeDataProvider = async ( + sourceAssetDenom: string, + sourceAssetChainId: string +) => { + const bridgeDataFetcher = BridgeDataFetcher.getInstance(); + const bridgeData = await bridgeDataFetcher.fetchBridgeData( + sourceAssetDenom, + sourceAssetChainId + ); + + const ibcAssetData = bridgeData.dest_assets[ + sourceAssetChainId + ]?.assets?.find(({ origin_denom }) => origin_denom === sourceAssetDenom); + + if (!ibcAssetData) { + throw new Error("No IBC asset data"); + } + + const channelId = ibcAssetData.trace.split("/")[0]; + + if (!channelId) { + throw new Error("No channel for bridge"); + } + + return { + channelId, + ibcDenom: ibcAssetData.denom, + portId: "transfer", + }; +}; diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts new file mode 100644 index 00000000000..1ba1e7a6d74 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts @@ -0,0 +1,129 @@ +import { + convertDisplayUnitToBaseUnit, + getAssetBySymbol, + getChainByChainName, +} from "@chain-registry/utils"; +import { assets, chains } from "chain-registry"; +import { getPaidFeeFromReceipt } from "../../../shared/helpers/cosmos-transaction-receipt.ts"; +import type { + IBridgeDataProvider, + ICosmosActionService, + ICosmosPluginCustomChainData, + ICosmosTransaction, + ICosmosWalletChains, +} from "../../../shared/interfaces.ts"; +import { CosmosTransactionFeeEstimator } from "../../../shared/services/cosmos-transaction-fee-estimator.ts"; +import { getAvailableAssets } from "../../../shared/helpers/cosmos-assets.ts"; +import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; +import { IBCTransferActionParams } from "../types.ts"; + +export class CosmosIBCTransferAction implements ICosmosActionService { + constructor(private cosmosWalletChains: ICosmosWalletChains) { + this.cosmosWalletChains = cosmosWalletChains; + } + + async execute( + params: IBCTransferActionParams, + bridgeDataProvider: IBridgeDataProvider, + customChainAssets?: ICosmosPluginCustomChainData["assets"][] + ): Promise { + const signingCosmWasmClient = + this.cosmosWalletChains.getSigningCosmWasmClient(params.chainName); + + const senderAddress = await this.cosmosWalletChains.getWalletAddress( + params.chainName + ); + + if (!senderAddress) { + throw new Error( + `Cannot get wallet address for chain ${params.chainName}` + ); + } + + if (!params.toAddress) { + throw new Error("No receiver address"); + } + + if (!params.targetChainName) { + throw new Error("No target chain name"); + } + + if (!params.chainName) { + throw new Error("No chain name"); + } + + if (!params.symbol) { + throw new Error("No symbol"); + } + + const availableAssets = getAvailableAssets(assets, customChainAssets); + + const denom = getAssetBySymbol( + availableAssets, + params.symbol, + params.chainName + ); + + const chain = getChainByChainName(chains, params.chainName); + + if (!denom.base) { + throw new Error("Cannot find asset"); + } + if (!chain) { + throw new Error("Cannot find chain"); + } + + const bridgeData = await bridgeDataProvider(denom.base, chain.chain_id); + + const now = BigInt(Date.now()) * BigInt(1_000_000); + const timeout = now + BigInt(5 * 60 * 1_000_000_000); + + const token: MsgTransfer["token"] = { + denom: bridgeData.ibcDenom, + amount: convertDisplayUnitToBaseUnit( + availableAssets, + params.symbol, + params.amount + ), + }; + + const message: MsgTransfer = { + sender: senderAddress, + receiver: params.toAddress, + sourceChannel: bridgeData.channelId, + sourcePort: bridgeData.portId, + timeoutTimestamp: timeout, + timeoutHeight: { + revisionHeight: BigInt(0), + revisionNumber: BigInt(0), + }, + token, + memo: "", + }; + + const gasFee = + await CosmosTransactionFeeEstimator.estimateGasForIBCTransfer( + signingCosmWasmClient, + message + ); + + const txDeliveryResponse = await signingCosmWasmClient.sendTokens( + senderAddress, + params.toAddress, + [token], + { + gas: gasFee.toString(), + amount: [{ ...token, amount: gasFee.toString() }], + } + ); + + const gasPaid = getPaidFeeFromReceipt(txDeliveryResponse); + + return { + from: senderAddress, + to: params.toAddress, + gasPaid, + txHash: txDeliveryResponse.transactionHash, + }; + } +} diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts new file mode 100644 index 00000000000..ba62b9cd48d --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; +import { + bridgeDataProviderParamsSchema, + bridgeDataProviderResponseAssetsSchema, + bridgeDataProviderResponseSchema, + IBCTransferParamsSchema, +} from "./schema"; + +export type IBCTransferActionParams = z.infer; +export type BridgeDataProviderParams = z.infer< + typeof bridgeDataProviderParamsSchema +>; +export type BridgeDataProviderResponseAsset = z.infer< + typeof bridgeDataProviderResponseAssetsSchema +>; +export type BridgeDataProviderResponse = z.infer< + typeof bridgeDataProviderResponseSchema +>; diff --git a/packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts b/packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts new file mode 100644 index 00000000000..af11af53d62 --- /dev/null +++ b/packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts @@ -0,0 +1,11 @@ +import { MsgTransferEncodeObject } from "@cosmjs/stargate"; +import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; + +export const generateIbcTransferMessage = ( + ibcTransferParams: MsgTransfer +): MsgTransferEncodeObject => { + return { + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: ibcTransferParams, + }; +}; diff --git a/packages/plugin-cosmos/src/shared/interfaces.ts b/packages/plugin-cosmos/src/shared/interfaces.ts index 13b9b003a37..0c28abf2332 100644 --- a/packages/plugin-cosmos/src/shared/interfaces.ts +++ b/packages/plugin-cosmos/src/shared/interfaces.ts @@ -44,3 +44,10 @@ export interface ICosmosWalletChains { export interface ICosmosWalletChainsData { [chainName: string]: ICosmosChainWallet; } + +export interface IBridgeDataProvider { + ( + sourceAssetDenom: string, + sourceAssetChainId: string + ): Promise<{ channelId: string; portId: string; ibcDenom: string }>; +} diff --git a/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts b/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts index d9a09c29ffb..9016a5e916d 100644 --- a/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts +++ b/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts @@ -1,6 +1,8 @@ import type { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import type { EncodeObject } from "@cosmjs/proto-signing"; import type { Coin, MsgSendEncodeObject } from "@cosmjs/stargate"; +import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; +import { generateIbcTransferMessage } from "../helpers/cosmos-messages"; export class CosmosTransactionFeeEstimator { private static async estimateGasForTransaction< @@ -46,4 +48,16 @@ export class CosmosTransactionFeeEstimator { memo ); } + static estimateGasForIBCTransfer( + signingCosmWasmClient: SigningCosmWasmClient, + ibcTransferParams: MsgTransfer, + memo = "" + ): Promise { + return this.estimateGasForTransaction( + signingCosmWasmClient, + ibcTransferParams.sender, + [generateIbcTransferMessage(ibcTransferParams)], + memo + ); + } } diff --git a/packages/plugin-cosmos/src/templates/index.ts b/packages/plugin-cosmos/src/templates/index.ts index 44fdf98aa0a..136a5dc78d1 100644 --- a/packages/plugin-cosmos/src/templates/index.ts +++ b/packages/plugin-cosmos/src/templates/index.ts @@ -34,6 +34,53 @@ Example reponse for the input: "Make transfer 0.0001 OM to mantra1pcnw46km8m5amv "toAddress": "mantra1pcnw46km8m5amvf7jlk2ks5std75k73aralhcf", "chainName": "mantrachaintestnet2" \`\`\` +Now respond with a JSON markdown block containing only the extracted values. +`; + +export const cosmosIBCTransferTemplate = `Given the recent messages and cosmos wallet information below: +{{recentMessages}} +{{walletInfo}} +Extract the following information about the requested IBC transfer: +1. **Amount**: + - Extract only the numeric value from the instruction. + - The value must be a string representing the amount in the display denomination (e.g., "0.0001" for ATOM, OSMO, etc.). Do not include the symbol. + +2. **Recipient Address**: + - Must be a valid Bech32 address that matches the target chain's address prefix. + - Example for "cosmoshub": "cosmos1pcnw46km8m5amvf7jlk2ks5std75k73aralhcf". + +3. **Token Symbol**: + - The symbol must be a string representing the token's display denomination (e.g., "ATOM", "OSMO", etc.). + +4. **Source Chain Name**: + - Identify the source chain mentioned in the instruction (e.g., cosmoshub, osmosis, axelar). + - Provide this as a string. + +5. **Target Chain Name**: + - Identify the target chain mentioned in the instruction (e.g., cosmoshub, osmosis, axelar). + - Provide this as a string. + +Respond with a JSON markdown block containing only the extracted values. All fields are required: +\`\`\`json +{ + "symbol": string, // The symbol of the token. + "amount": string, // The amount to transfer as a string. + "toAddress": string, // The recipient's address. + "chainName": string, // The source chain name. + "targetChainName": string // The target chain name. +} +\`\`\` + +Example response for the input: "Make an IBC transfer of 0.0001 ATOM to osmo1pcnw46km8m5amvf7jlk2ks5std75k73aralhcf from cosmoshub to osmosis", the response should be: +\`\`\`json +{ + "symbol": "ATOM", + "amount": "0.0001", + "toAddress": "osmo1pcnw46km8m5amvf7jlk2ks5std75k73aralhcf", + "chainName": "cosmoshub", + "targetChainName": "osmosis" +} +\`\`\` Now respond with a JSON markdown block containing only the extracted values. `; diff --git a/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts b/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts new file mode 100644 index 00000000000..406d8bd032a --- /dev/null +++ b/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { BridgeDataFetcher } from "../actions/ibc-transfer/services/bridge-data-fetcher"; +import axios from "axios"; + +vi.mock("axios"); + +describe("BridgeDataFetcher", () => { + let fetcher: BridgeDataFetcher; + + beforeEach(() => { + fetcher = BridgeDataFetcher.getInstance(); + vi.clearAllMocks(); + }); + + it("should return the same instance from getInstance", () => { + const fetcher1 = BridgeDataFetcher.getInstance(); + const fetcher2 = BridgeDataFetcher.getInstance(); + expect(fetcher1).toBe(fetcher2); + }); + + it("should use cache when data is already fetched", async () => { + const mockResponse = { + dest_assets: { + someKey: { + assets: [ + { + denom: "atom", + chain_id: "cosmos", + origin_denom: "atom", + origin_chain_id: "cosmos", + trace: "someTrace", + symbol: "ATOM", + name: "Cosmos Atom", + logo_uri: "http://someurl.com/logo.png", + decimals: 6, + recommended_symbol: "ATOM", + }, + ], + }, + }, + }; + + // @ts-expect-error -- ... + axios.post.mockResolvedValueOnce({ data: mockResponse }); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); + + expect(axios.post).toHaveBeenCalledTimes(1); + + await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); + expect(axios.post).toHaveBeenCalledTimes(1); // axios nie powinien być wywołany ponownie + }); + + it("should fetch and cache data correctly", async () => { + const mockResponse = { + dest_assets: { + someKey: { + assets: [ + { + denom: "atom", + chain_id: "cosmos", + origin_denom: "atom", + origin_chain_id: "cosmos", + trace: "someTrace", + symbol: "ATOM", + name: "Cosmos Atom", + logo_uri: "http://someurl.com/logo.png", + decimals: 6, + recommended_symbol: "ATOM", + }, + ], + }, + }, + }; + + // @ts-expect-error -- ... + axios.post.mockResolvedValueOnce({ data: mockResponse }); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + const result = await fetcher.fetchBridgeData( + sourceAssetDenom, + sourceAssetChainId + ); + + expect(result).toEqual(mockResponse); + + const cacheKey = `${sourceAssetDenom}_${sourceAssetChainId}`; + expect(fetcher["cache"].has(cacheKey)).toBe(true); + + const cachedResult = await fetcher.fetchBridgeData( + sourceAssetDenom, + sourceAssetChainId + ); + expect(cachedResult).toEqual(mockResponse); + }); +}); diff --git a/packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts b/packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts new file mode 100644 index 00000000000..f2834ad09a1 --- /dev/null +++ b/packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts @@ -0,0 +1,103 @@ +import { bridgeDataProvider } from "../actions/ibc-transfer/services/bridge-data-provider"; +import { BridgeDataFetcher } from "../actions/ibc-transfer/services/bridge-data-fetcher"; +import { vi, expect, it, beforeEach, describe } from "vitest"; + +vi.mock("./bridge-data-fetcher", () => ({ + BridgeDataFetcher: { + getInstance: vi.fn().mockReturnValue({ + fetchBridgeData: vi.fn(), + }), + }, +})); + +describe("bridgeDataProvider", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let mockFetchBridgeData: any; + + beforeEach(() => { + mockFetchBridgeData = vi.fn(); + BridgeDataFetcher.getInstance().fetchBridgeData = mockFetchBridgeData; + }); + + it("should return correct channelId and ibcDenom when valid data is returned", async () => { + const mockResponse = { + dest_assets: { + cosmos: { + assets: [ + { + origin_denom: "atom", + denom: "uatom", + trace: "channel-123/abc", + }, + ], + }, + }, + }; + + mockFetchBridgeData.mockResolvedValue(mockResponse); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + const result = await bridgeDataProvider( + sourceAssetDenom, + sourceAssetChainId + ); + + expect(result).toEqual({ + channelId: "channel-123", + ibcDenom: "uatom", + portId: "transfer", + }); + }); + + it("should throw an error when ibcAssetData is not found", async () => { + const mockResponse = { + dest_assets: { + cosmos: { + assets: [ + { + origin_denom: "btc", + denom: "ubtc", + trace: "channel-123/abc", + }, + ], + }, + }, + }; + + mockFetchBridgeData.mockResolvedValue(mockResponse); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + await expect( + bridgeDataProvider(sourceAssetDenom, sourceAssetChainId) + ).rejects.toThrowError(); + }); + + it("should throw an error when channelId is missing", async () => { + const mockResponse = { + dest_assets: { + cosmos: { + assets: [ + { + origin_denom: "atom", + denom: "uatom", + trace: "", + }, + ], + }, + }, + }; + + mockFetchBridgeData.mockResolvedValue(mockResponse); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + await expect( + bridgeDataProvider(sourceAssetDenom, sourceAssetChainId) + ).rejects.toThrowError(); + }); +}); diff --git a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts new file mode 100644 index 00000000000..fc7fbd5e6e0 --- /dev/null +++ b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts @@ -0,0 +1,309 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { CosmosIBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; +import { IBCTransferActionParams } from "../actions/ibc-transfer/types"; +import { getAssetBySymbol, getChainByChainName } from "@chain-registry/utils"; + +vi.mock("@cosmjs/cosmwasm-stargate", () => ({ + SigningCosmWasmClient: { + connectWithSigner: vi.fn(), + }, +})); + +vi.mock("@chain-registry/utils", () => ({ + getAssetBySymbol: vi.fn().mockReturnValue({ base: "uatom" }), + getChainByChainName: vi + .fn() + .mockResolvedValue({ chain_id: "cosmos-chain-id" }), + convertDisplayUnitToBaseUnit: vi.fn().mockResolvedValue("100000000"), +})); + +vi.mock("../shared/services/cosmos-transaction-fee-estimator", () => ({ + CosmosTransactionFeeEstimator: { + estimateGasForIBCTransfer: vi.fn().mockResolvedValue(BigInt(200_000)), + }, +})); + +vi.mock("../shared/helpers/cosmos-assets", () => ({ + getAvailableAssets: vi.fn().mockResolvedValue([]), +})); + +vi.mock("../shared/helpers/cosmos-transaction-receipt.ts", () => ({ + getPaidFeeFromReceipt: vi.fn().mockReturnValue("200000"), +})); + +describe("CosmosIBCTransferAction", () => { + const mockSigningCosmWasmClient = { + sendTokens: vi.fn().mockResolvedValue({ + transactionHash: "mockTxHash", + }), + }; + + const mockCosmosWalletChains = { + walletChainsData: {}, + getWalletAddress: vi.fn().mockResolvedValue("senderAddress"), + getSigningCosmWasmClient: vi + .fn() + .mockReturnValue(mockSigningCosmWasmClient), + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("should execute transfer successfully", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + const expectedResult = { + from: "senderAddress", + to: "cosmosReceiverAddress", + gasPaid: "200000", + txHash: "mockTxHash", + }; + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).resolves.toEqual(expectedResult); + }); + + it("should throw error if transaction fails", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + + mockSigningCosmWasmClient.sendTokens.mockRejectedValue( + new Error("Transaction Failed") + ); + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("Transaction Failed"); + }); + + it("should throw error if no wallet address is found", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + + mockCosmosWalletChains.getWalletAddress.mockResolvedValue(null); // Brak adresu portfela + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("Cannot get wallet address for chain cosmos"); + }); + + it("should throw error if no receiver address is provided", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + mockCosmosWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1address" + ); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("No receiver address"); + }); + + it("should throw error if no symbol is provided", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "", + amount: "100", + }; + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + mockCosmosWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1address" + ); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("No symbol"); + }); + + it("should throw error if no chainName is provided", async () => { + const params: IBCTransferActionParams = { + chainName: "", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + mockCosmosWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1address" + ); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("No chain name"); + }); + it("should throw error if no targetChainName is provided", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "", + symbol: "atom", + amount: "100", + }; + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + mockCosmosWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1address" + ); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("No target chain name"); + }); + it("should throw error if no base denom in assets list", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + + // @ts-expect-error --- + getAssetBySymbol.mockReturnValue({}); + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + mockCosmosWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1address" + ); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("Cannot find asset"); + }); + + it("should throw error if no chain in chain list", async () => { + const params: IBCTransferActionParams = { + chainName: "cosmos", + toAddress: "cosmosReceiverAddress", + targetChainName: "cosmosTarget", + symbol: "atom", + amount: "100", + }; + // @ts-expect-error --- ... + getAssetBySymbol.mockReturnValue({ base: "uatom" }); + // @ts-expect-error --- ... + getChainByChainName.mockReturnValue(undefined); + + const mockBridgeDataProvider = vi.fn().mockResolvedValue({ + ibcDenom: "uatom", + channelId: "channel-1", + portId: "transfer", + }); + + mockCosmosWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1address" + ); + + const cosmosIBCTransferAction = new CosmosIBCTransferAction( + mockCosmosWalletChains + ); + + await expect( + cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ).rejects.toThrow("Cannot find chain"); + }); +}); diff --git a/packages/plugin-cosmos/src/tests/cosmos-message.test.ts b/packages/plugin-cosmos/src/tests/cosmos-message.test.ts new file mode 100644 index 00000000000..3a5a46c3d21 --- /dev/null +++ b/packages/plugin-cosmos/src/tests/cosmos-message.test.ts @@ -0,0 +1,33 @@ +import { describe, it, expect } from "vitest"; +import { generateIbcTransferMessage } from "../shared/helpers/cosmos-messages"; +import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; +import { MsgTransferEncodeObject } from "@cosmjs/stargate"; + +describe("generateIbcTransferMessage", () => { + it("should return a correctly formatted MsgTransferEncodeObject", () => { + const ibcTransferParams: MsgTransfer = { + sourcePort: "transfer", + sourceChannel: "channel-0", + token: { + denom: "uatom", + amount: "1000", + }, + sender: "cosmos1...", + receiver: "cosmos2...", + timeoutHeight: { + revisionHeight: BigInt(1000), + revisionNumber: BigInt(1), + }, + timeoutTimestamp: BigInt(1625140800), + memo: "", + }; + + const result: MsgTransferEncodeObject = + generateIbcTransferMessage(ibcTransferParams); + + expect(result).toEqual({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: ibcTransferParams, + }); + }); +}); diff --git a/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts b/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts index bbbd3cc55ef..ed10781ecfe 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts @@ -1,6 +1,8 @@ import { describe, it, expect, vi, beforeEach, Mock } from "vitest"; import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { CosmosTransactionFeeEstimator } from "../shared/services/cosmos-transaction-fee-estimator"; +import { generateIbcTransferMessage } from "../shared/helpers/cosmos-messages"; +import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; vi.mock("@cosmjs/cosmwasm-stargate", () => ({ SigningCosmWasmClient: { @@ -8,6 +10,10 @@ vi.mock("@cosmjs/cosmwasm-stargate", () => ({ }, })); +vi.mock("../shared/helpers/cosmos-messages", () => ({ + generateIbcTransferMessage: vi.fn(), +})); + describe("FeeEstimator", () => { let mockSigningCosmWasmClient: SigningCosmWasmClient; @@ -91,4 +97,100 @@ describe("FeeEstimator", () => { "" ); }); + + it("should estimate gas for an IBC transfer successfully", async () => { + const mockGasEstimation = 300000; + + (mockSigningCosmWasmClient.simulate as Mock).mockResolvedValue( + mockGasEstimation + ); + + const ibcTransferParams: MsgTransfer = { + sourcePort: "transfer", + sourceChannel: "channel-0", + token: { denom: "uatom", amount: "1000000" }, + sender: "cosmos1senderaddress", + receiver: "cosmos1recipientaddress", + timeoutHeight: { + revisionNumber: BigInt(1), + revisionHeight: BigInt(1000), + }, + timeoutTimestamp: BigInt(0), + memo: "", + }; + + (generateIbcTransferMessage as Mock).mockReturnValue({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: ibcTransferParams, + }); + + const memo = "IBC Test Memo"; + + const estimatedGas = + await CosmosTransactionFeeEstimator.estimateGasForIBCTransfer( + mockSigningCosmWasmClient, + ibcTransferParams, + memo + ); + + // Add 20% to the estimated gas to make sure we have enough gas to cover the transaction + expect(estimatedGas).toBe(mockGasEstimation + mockGasEstimation * 0.2); + + expect(mockSigningCosmWasmClient.simulate).toHaveBeenCalledWith( + ibcTransferParams.sender, + [ + { + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: ibcTransferParams, + }, + ], + memo + ); + expect(generateIbcTransferMessage).toHaveBeenCalledWith( + ibcTransferParams + ); + }); + + it("should throw an error if gas estimation for IBC transfer fails", async () => { + (mockSigningCosmWasmClient.simulate as Mock).mockRejectedValue( + new Error("IBC gas estimation failed") + ); + + const ibcTransferParams: MsgTransfer = { + sourcePort: "transfer", + sourceChannel: "channel-0", + token: { denom: "uatom", amount: "1000000" }, + sender: "cosmos1senderaddress", + receiver: "cosmos1recipientaddress", + timeoutHeight: { + revisionNumber: BigInt(1), + revisionHeight: BigInt(1000), + }, + timeoutTimestamp: BigInt(0), + memo: "", + }; + + (generateIbcTransferMessage as Mock).mockReturnValue({ + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: ibcTransferParams, + }); + + await expect( + CosmosTransactionFeeEstimator.estimateGasForIBCTransfer( + mockSigningCosmWasmClient, + ibcTransferParams + ) + ).rejects.toThrow("IBC gas estimation failed"); + + expect(mockSigningCosmWasmClient.simulate).toHaveBeenCalledWith( + ibcTransferParams.sender, + [ + { + typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", + value: ibcTransferParams, + }, + ], + "" + ); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dde34c9e572..7e49154580d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1200,18 +1200,28 @@ importers: '@elizaos/core': specifier: workspace:* version: link:../core + axios: + specifier: ^1.7.9 + version: 1.7.9(debug@4.4.0) bignumber.js: specifier: 9.1.2 version: 9.1.2 chain-registry: specifier: ^1.69.68 version: 1.69.86 + interchain: + specifier: ^1.10.4 + version: 1.10.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) tsup: specifier: 8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.6.3)(yaml@2.7.0) zod: specifier: 3.23.8 version: 3.23.8 + devDependencies: + '@chain-registry/types': + specifier: ^0.50.44 + version: 0.50.45 packages/plugin-cronoszkevm: dependencies: @@ -3499,6 +3509,7 @@ packages: '@confio/ics23@0.6.8': resolution: {integrity: sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==} + deprecated: Unmaintained. The codebase for this package was moved to https://github.com/cosmos/ics23 but then the JS implementation was removed in https://github.com/cosmos/ics23/pull/353. Please consult the maintainers of https://github.com/cosmos for further assistance. '@coral-xyz/anchor-errors@0.30.1': resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} @@ -3550,6 +3561,9 @@ packages: peerDependencies: '@solana/web3.js': ^1.68.0 + '@cosmjs/amino@0.32.2': + resolution: {integrity: sha512-lcK5RCVm4OfdAooxKcF2+NwaDVVpghOq6o/A40c2mHXDUzUoRZ33VAHjVJ9Me6vOFxshrw/XEFn1f4KObntjYA==} + '@cosmjs/amino@0.32.4': resolution: {integrity: sha512-zKYOt6hPy8obIFtLie/xtygCkH9ZROiQ12UHfKsOkWaZfPQUvVbtgmu6R4Kn1tFLI/SRkw7eqhaogmW/3NYu/Q==} @@ -3568,24 +3582,36 @@ packages: '@cosmjs/math@0.32.4': resolution: {integrity: sha512-++dqq2TJkoB8zsPVYCvrt88oJWsy1vMOuSOKcdlnXuOA/ASheTJuYy4+oZlTQ3Fr8eALDLGGPhJI02W2HyAQaw==} + '@cosmjs/proto-signing@0.32.2': + resolution: {integrity: sha512-UV4WwkE3W3G3s7wwU9rizNcUEz2g0W8jQZS5J6/3fiN0mRPwtPKQ6EinPN9ASqcAJ7/VQH4/9EPOw7d6XQGnqw==} + '@cosmjs/proto-signing@0.32.4': resolution: {integrity: sha512-QdyQDbezvdRI4xxSlyM1rSVBO2st5sqtbEIl3IX03uJ7YiZIQHyv6vaHVf1V4mapusCqguiHJzm4N4gsFdLBbQ==} '@cosmjs/socket@0.32.4': resolution: {integrity: sha512-davcyYziBhkzfXQTu1l5NrpDYv0K9GekZCC9apBRvL1dvMc9F/ygM7iemHjUA+z8tJkxKxrt/YPjJ6XNHzLrkw==} + '@cosmjs/stargate@0.32.2': + resolution: {integrity: sha512-AsJa29fT7Jd4xt9Ai+HMqhyj7UQu7fyYKdXj/8+/9PD74xe6lZSYhQPcitUmMLJ1ckKPgXSk5Dd2LbsQT0IhZg==} + '@cosmjs/stargate@0.32.4': resolution: {integrity: sha512-usj08LxBSsPRq9sbpCeVdyLx2guEcOHfJS9mHGCLCXpdAPEIEQEtWLDpEUc0LEhWOx6+k/ChXTc5NpFkdrtGUQ==} '@cosmjs/stream@0.32.4': resolution: {integrity: sha512-Gih++NYHEiP+oyD4jNEUxU9antoC0pFSg+33Hpp0JlHwH0wXhtD3OOKnzSfDB7OIoEbrzLJUpEjOgpCp5Z+W3A==} + '@cosmjs/tendermint-rpc@0.32.2': + resolution: {integrity: sha512-DXyJHDmcAfCix4H/7/dKR0UMdshP01KxJOXHdHxBCbLIpck94BsWD3B2ZTXwfA6sv98so9wOzhp7qGQa5malxg==} + '@cosmjs/tendermint-rpc@0.32.4': resolution: {integrity: sha512-MWvUUno+4bCb/LmlMIErLypXxy7ckUuzEmpufYYYd9wgbdCXaTaO08SZzyFM5PI8UJ/0S2AmUrgWhldlbxO8mw==} '@cosmjs/utils@0.32.4': resolution: {integrity: sha512-D1Yc+Zy8oL/hkUkFUL/bwxvuDBzRGpc4cF7/SkdhxX4iHpSLgdOuTt1mhCh9+kl6NQREy9t7SYZ6xeW5gFe60w==} + '@cosmology/lcd@0.13.5': + resolution: {integrity: sha512-CI8KFsJcgp0RINF8wHpv3Y9yR4Fb9ZnGucyoUICjtX2XT4NVBK+fvZuRFj5TP34km8TpEOb+WV2T7IN/pZsD7Q==} + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -13587,6 +13613,9 @@ packages: int64-buffer@0.1.10: resolution: {integrity: sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==} + interchain@1.10.4: + resolution: {integrity: sha512-tyJ3mfcuYqwLb3iZyuXDMOwMjWYptgiZrl6tu50pSSYoWrPN/9B6ztEC4IkYT1oKmWVOAiacNYuSRNmMUuWsmA==} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -23137,6 +23166,13 @@ snapshots: bn.js: 5.2.1 buffer-layout: 1.2.2 + '@cosmjs/amino@0.32.2': + dependencies: + '@cosmjs/crypto': 0.32.4 + '@cosmjs/encoding': 0.32.4 + '@cosmjs/math': 0.32.4 + '@cosmjs/utils': 0.32.4 + '@cosmjs/amino@0.32.4': dependencies: '@cosmjs/crypto': 0.32.4 @@ -23186,6 +23222,15 @@ snapshots: dependencies: bn.js: 5.2.1 + '@cosmjs/proto-signing@0.32.2': + dependencies: + '@cosmjs/amino': 0.32.4 + '@cosmjs/crypto': 0.32.4 + '@cosmjs/encoding': 0.32.4 + '@cosmjs/math': 0.32.4 + '@cosmjs/utils': 0.32.4 + cosmjs-types: 0.9.0 + '@cosmjs/proto-signing@0.32.4': dependencies: '@cosmjs/amino': 0.32.4 @@ -23205,6 +23250,23 @@ snapshots: - bufferutil - utf-8-validate + '@cosmjs/stargate@0.32.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@confio/ics23': 0.6.8 + '@cosmjs/amino': 0.32.4 + '@cosmjs/encoding': 0.32.4 + '@cosmjs/math': 0.32.4 + '@cosmjs/proto-signing': 0.32.4 + '@cosmjs/stream': 0.32.4 + '@cosmjs/tendermint-rpc': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/utils': 0.32.4 + cosmjs-types: 0.9.0 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/stargate@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@confio/ics23': 0.6.8 @@ -23226,6 +23288,23 @@ snapshots: dependencies: xstream: 11.14.0 + '@cosmjs/tendermint-rpc@0.32.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@cosmjs/crypto': 0.32.4 + '@cosmjs/encoding': 0.32.4 + '@cosmjs/json-rpc': 0.32.4 + '@cosmjs/math': 0.32.4 + '@cosmjs/socket': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/stream': 0.32.4 + '@cosmjs/utils': 0.32.4 + axios: 1.7.9(debug@4.4.0) + readonly-date: 1.0.0 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/tendermint-rpc@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@cosmjs/crypto': 0.32.4 @@ -23245,6 +23324,12 @@ snapshots: '@cosmjs/utils@0.32.4': {} + '@cosmology/lcd@0.13.5': + dependencies: + axios: 1.7.4 + transitivePeerDependencies: + - debug + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -36319,7 +36404,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.4 + debug: 4.4.0(supports-color@8.1.1) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -37906,6 +37991,18 @@ snapshots: int64-buffer@0.1.10: {} + interchain@1.10.4(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@cosmjs/amino': 0.32.2 + '@cosmjs/proto-signing': 0.32.2 + '@cosmjs/stargate': 0.32.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/tendermint-rpc': 0.32.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmology/lcd': 0.13.5 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 From 6a929a10131ff489d63d4b15520bfd5d216ef31b Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Wed, 8 Jan 2025 16:53:09 +0100 Subject: [PATCH 02/12] refactor: remove redundant services and use skipClient --- packages/plugin-cosmos/package.json | 1 + .../src/actions/ibc-transfer/index.ts | 6 +- .../src/actions/ibc-transfer/schema.ts | 28 ----- .../services/bridge-data-fetcher.ts | 68 ------------ .../services/bridge-data-provider.ts | 33 ------ .../services/ibc-transfer-action-service.ts | 97 +++++++---------- .../src/actions/ibc-transfer/types.ts | 16 +-- .../entities/cosmos-wallet-chains-data.ts | 14 +++ .../src/shared/helpers/cosmos-messages.ts | 11 -- .../plugin-cosmos/src/shared/interfaces.ts | 5 +- .../cosmos-transaction-fee-estimator.ts | 14 --- .../src/tests/bridge-data-fetcher.test.ts | 101 ----------------- .../src/tests/bridge-data-provider.test.ts | 103 ------------------ ...cosmos-ibc-transfer-action-service.test.ts | 22 ++-- .../src/tests/cosmos-message.test.ts | 33 ------ .../cosmos-transaction-fee-estimator.test.ts | 98 ----------------- packages/plugin-cosmos/tsup.config.ts | 1 + 17 files changed, 75 insertions(+), 576 deletions(-) delete mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts delete mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts delete mode 100644 packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts delete mode 100644 packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts delete mode 100644 packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts delete mode 100644 packages/plugin-cosmos/src/tests/cosmos-message.test.ts diff --git a/packages/plugin-cosmos/package.json b/packages/plugin-cosmos/package.json index b052b3060ed..90004ab76bd 100644 --- a/packages/plugin-cosmos/package.json +++ b/packages/plugin-cosmos/package.json @@ -10,6 +10,7 @@ "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/proto-signing": "^0.32.4", "@cosmjs/stargate": "^0.32.4", + "@skip-go/client": "^0.16.3", "axios": "^1.7.9", "bignumber.js": "9.1.2", "chain-registry": "^1.69.68", diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts index f0833c3983b..687d315f11f 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -17,8 +17,7 @@ import type { ICosmosWalletChains, } from "../../shared/interfaces"; import { IBCTransferActionParams } from "./types"; -import { CosmosIBCTransferAction } from "./services/ibc-transfer-action-service"; -import { bridgeDataProvider } from "./services/bridge-data-provider"; +import { IBCTransferAction } from "./services/ibc-transfer-action-service"; export const createIBCTransferAction = ( pluginOptions: ICosmosPluginOptions @@ -56,7 +55,7 @@ export const createIBCTransferAction = ( const walletProvider: ICosmosWalletChains = await initWalletChainsData(_runtime); - const action = new CosmosIBCTransferAction(walletProvider); + const action = new IBCTransferAction(walletProvider); const customAssets = (pluginOptions?.customChainData ?? []).map( (chainData) => chainData.assets @@ -64,7 +63,6 @@ export const createIBCTransferAction = ( const transferResp = await action.execute( paramOptions, - bridgeDataProvider, customAssets ); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts index 018e892d242..62e30be681d 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts @@ -7,31 +7,3 @@ export const IBCTransferParamsSchema = z.object({ toAddress: z.string(), targetChainName: z.string(), }); - -export const bridgeDataProviderParamsSchema = z.object({ - source_asset_denom: z.string(), - source_asset_chain_id: z.string(), - allow_multi_tx: z.boolean(), -}); - -export const bridgeDataProviderResponseAssetsSchema = z.object({ - denom: z.string(), - chain_id: z.string(), - origin_denom: z.string(), - origin_chain_id: z.string(), - trace: z.string(), - symbol: z.string().optional(), - name: z.string().optional(), - logo_uri: z.string().optional(), - decimals: z.number().optional(), - recommended_symbol: z.string().optional(), -}); - -export const bridgeDataProviderResponseSchema = z.object({ - dest_assets: z.record( - z.string(), - z.object({ - assets: z.array(bridgeDataProviderResponseAssetsSchema), - }) - ), -}); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts deleted file mode 100644 index 1ababb35214..00000000000 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-fetcher.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { bridgeDataProviderResponseSchema } from "../schema"; -import { BridgeDataProviderParams, BridgeDataProviderResponse } from "../types"; -import axios from "axios"; - -type CacheKey = `${string}_${string}`; - -export class BridgeDataFetcher { - private static instance: BridgeDataFetcher; - private cache: Map; - private readonly apiUrl: string; - - private constructor() { - this.cache = new Map(); - this.apiUrl = "https://api.skip.build/v2/fungible/assets_from_source"; - } - - public static getInstance(): BridgeDataFetcher { - if (!BridgeDataFetcher.instance) { - BridgeDataFetcher.instance = new BridgeDataFetcher(); - } - return BridgeDataFetcher.instance; - } - - private generateCacheKey( - sourceAssetDenom: string, - sourceAssetChainId: string - ): CacheKey { - return `${sourceAssetDenom}_${sourceAssetChainId}`; - } - - public async fetchBridgeData( - sourceAssetDenom: string, - sourceAssetChainId: string - ): Promise { - const cacheKey = this.generateCacheKey( - sourceAssetDenom, - sourceAssetChainId - ); - - if (this.cache.has(cacheKey)) { - return this.cache.get(cacheKey)!; - } - - const requestData: BridgeDataProviderParams = { - source_asset_denom: sourceAssetDenom, - source_asset_chain_id: sourceAssetChainId, - allow_multi_tx: false, - }; - - try { - const response = await axios.post(this.apiUrl, requestData, { - headers: { - "Content-Type": "application/json", - }, - }); - - const validResponse = bridgeDataProviderResponseSchema.parse( - response.data - ); - - this.cache.set(cacheKey, validResponse); - return response.data; - } catch (error) { - console.error("Error fetching assets:", error); - throw error; - } - } -} diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts deleted file mode 100644 index b20cc68124e..00000000000 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-data-provider.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { IBridgeDataProvider } from "../../../shared/interfaces"; -import { BridgeDataFetcher } from "./bridge-data-fetcher"; - -export const bridgeDataProvider: IBridgeDataProvider = async ( - sourceAssetDenom: string, - sourceAssetChainId: string -) => { - const bridgeDataFetcher = BridgeDataFetcher.getInstance(); - const bridgeData = await bridgeDataFetcher.fetchBridgeData( - sourceAssetDenom, - sourceAssetChainId - ); - - const ibcAssetData = bridgeData.dest_assets[ - sourceAssetChainId - ]?.assets?.find(({ origin_denom }) => origin_denom === sourceAssetDenom); - - if (!ibcAssetData) { - throw new Error("No IBC asset data"); - } - - const channelId = ibcAssetData.trace.split("/")[0]; - - if (!channelId) { - throw new Error("No channel for bridge"); - } - - return { - channelId, - ibcDenom: ibcAssetData.denom, - portId: "transfer", - }; -}; diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts index 1ba1e7a6d74..7a132eb2995 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts @@ -1,39 +1,35 @@ import { - convertDisplayUnitToBaseUnit, getAssetBySymbol, getChainByChainName, + getChainIdByChainName, } from "@chain-registry/utils"; import { assets, chains } from "chain-registry"; -import { getPaidFeeFromReceipt } from "../../../shared/helpers/cosmos-transaction-receipt.ts"; import type { - IBridgeDataProvider, ICosmosActionService, ICosmosPluginCustomChainData, ICosmosTransaction, ICosmosWalletChains, } from "../../../shared/interfaces.ts"; -import { CosmosTransactionFeeEstimator } from "../../../shared/services/cosmos-transaction-fee-estimator.ts"; import { getAvailableAssets } from "../../../shared/helpers/cosmos-assets.ts"; -import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; import { IBCTransferActionParams } from "../types.ts"; -export class CosmosIBCTransferAction implements ICosmosActionService { +export class IBCTransferAction implements ICosmosActionService { constructor(private cosmosWalletChains: ICosmosWalletChains) { this.cosmosWalletChains = cosmosWalletChains; } async execute( params: IBCTransferActionParams, - bridgeDataProvider: IBridgeDataProvider, customChainAssets?: ICosmosPluginCustomChainData["assets"][] ): Promise { - const signingCosmWasmClient = - this.cosmosWalletChains.getSigningCosmWasmClient(params.chainName); - const senderAddress = await this.cosmosWalletChains.getWalletAddress( params.chainName ); + const skipClient = this.cosmosWalletChains.getSkipClient( + params.chainName + ); + if (!senderAddress) { throw new Error( `Cannot get wallet address for chain ${params.chainName}` @@ -64,66 +60,55 @@ export class CosmosIBCTransferAction implements ICosmosActionService { params.chainName ); - const chain = getChainByChainName(chains, params.chainName); + const sourceChain = getChainByChainName(chains, params.chainName); + const destChain = getChainByChainName(chains, params.targetChainName); if (!denom.base) { throw new Error("Cannot find asset"); } - if (!chain) { - throw new Error("Cannot find chain"); + if (!sourceChain) { + throw new Error("Cannot find source chain"); } - const bridgeData = await bridgeDataProvider(denom.base, chain.chain_id); + if (!destChain) { + throw new Error("Cannot find destination chain"); + } - const now = BigInt(Date.now()) * BigInt(1_000_000); - const timeout = now + BigInt(5 * 60 * 1_000_000_000); + const route = await skipClient.route({ + destAssetChainID: destChain.chain_id, + destAssetDenom: denom.base, + sourceAssetChainID: sourceChain.chain_id, + sourceAssetDenom: denom.base, + amountOut: params.amount, + }); + + const userAddresses = await Promise.all( + route.requiredChainAddresses.map(async (chainID) => { + const chainName = getChainIdByChainName(chains, chainID); + return { + chainID, + address: + await this.cosmosWalletChains.getWalletAddress( + chainName + ), + }; + }) + ); - const token: MsgTransfer["token"] = { - denom: bridgeData.ibcDenom, - amount: convertDisplayUnitToBaseUnit( - availableAssets, - params.symbol, - params.amount - ), - }; + let txHash: string | undefined; - const message: MsgTransfer = { - sender: senderAddress, - receiver: params.toAddress, - sourceChannel: bridgeData.channelId, - sourcePort: bridgeData.portId, - timeoutTimestamp: timeout, - timeoutHeight: { - revisionHeight: BigInt(0), - revisionNumber: BigInt(0), + await skipClient.executeRoute({ + route, + userAddresses, + onTransactionCompleted: async (_, executeRouteTxHash) => { + txHash = executeRouteTxHash; }, - token, - memo: "", - }; - - const gasFee = - await CosmosTransactionFeeEstimator.estimateGasForIBCTransfer( - signingCosmWasmClient, - message - ); - - const txDeliveryResponse = await signingCosmWasmClient.sendTokens( - senderAddress, - params.toAddress, - [token], - { - gas: gasFee.toString(), - amount: [{ ...token, amount: gasFee.toString() }], - } - ); - - const gasPaid = getPaidFeeFromReceipt(txDeliveryResponse); + }); return { from: senderAddress, to: params.toAddress, - gasPaid, - txHash: txDeliveryResponse.transactionHash, + txHash, }; } } diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts index ba62b9cd48d..349bfb2fb81 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts @@ -1,18 +1,4 @@ import { z } from "zod"; -import { - bridgeDataProviderParamsSchema, - bridgeDataProviderResponseAssetsSchema, - bridgeDataProviderResponseSchema, - IBCTransferParamsSchema, -} from "./schema"; +import { IBCTransferParamsSchema } from "./schema"; export type IBCTransferActionParams = z.infer; -export type BridgeDataProviderParams = z.infer< - typeof bridgeDataProviderParamsSchema ->; -export type BridgeDataProviderResponseAsset = z.infer< - typeof bridgeDataProviderResponseAssetsSchema ->; -export type BridgeDataProviderResponse = z.infer< - typeof bridgeDataProviderResponseSchema ->; diff --git a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts index fbe0322c270..3c4cc70f9a4 100644 --- a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts +++ b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts @@ -8,6 +8,7 @@ import type { ICosmosWalletChainsData, } from "../interfaces"; import { getAvailableChains } from "../helpers/cosmos-chains"; +import { SkipClient } from "@skip-go/client"; export class CosmosWalletChains implements ICosmosWalletChains { public walletChainsData: ICosmosWalletChainsData = {}; @@ -49,9 +50,14 @@ export class CosmosWalletChains implements ICosmosWalletChains { wallet.directSecp256k1HdWallet ); + const skipClient = new SkipClient({ + getCosmosSigner: async () => wallet.directSecp256k1HdWallet, + }); + walletChainsData[chainName] = { wallet, signingCosmWasmClient, + skipClient, }; } @@ -65,4 +71,12 @@ export class CosmosWalletChains implements ICosmosWalletChains { public getSigningCosmWasmClient(chainName: string) { return this.walletChainsData[chainName].signingCosmWasmClient; } + + public getSkipClient(chainName: string): SkipClient { + return this.walletChainsData[chainName].skipClient; + } + + public async getUserAddress(chainName: string): Promise { + return this.walletChainsData[chainName].wallet.getWalletAddress(); + } } diff --git a/packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts b/packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts deleted file mode 100644 index af11af53d62..00000000000 --- a/packages/plugin-cosmos/src/shared/helpers/cosmos-messages.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { MsgTransferEncodeObject } from "@cosmjs/stargate"; -import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; - -export const generateIbcTransferMessage = ( - ibcTransferParams: MsgTransfer -): MsgTransferEncodeObject => { - return { - typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - value: ibcTransferParams, - }; -}; diff --git a/packages/plugin-cosmos/src/shared/interfaces.ts b/packages/plugin-cosmos/src/shared/interfaces.ts index 0c28abf2332..2f4dc2438c3 100644 --- a/packages/plugin-cosmos/src/shared/interfaces.ts +++ b/packages/plugin-cosmos/src/shared/interfaces.ts @@ -1,6 +1,7 @@ import type { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import type { Coin, DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import type { assets, chains } from "chain-registry"; +import { SkipClient } from "@skip-go/client"; export interface ICosmosPluginCustomChainData { chainData: (typeof chains)[number]; @@ -19,7 +20,7 @@ export interface ICosmosTransaction { from: string; to: string; txHash: string; - gasPaid: number; + gasPaid?: number; } export interface ICosmosWallet { @@ -32,6 +33,7 @@ export interface ICosmosWallet { export interface ICosmosChainWallet { wallet: ICosmosWallet; signingCosmWasmClient: SigningCosmWasmClient; + skipClient: SkipClient; } export interface ICosmosWalletChains { @@ -39,6 +41,7 @@ export interface ICosmosWalletChains { getWalletAddress(chainName: string): Promise; getSigningCosmWasmClient(chainName: string): SigningCosmWasmClient; + getSkipClient(chainName: string): SkipClient; } export interface ICosmosWalletChainsData { diff --git a/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts b/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts index 9016a5e916d..d9a09c29ffb 100644 --- a/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts +++ b/packages/plugin-cosmos/src/shared/services/cosmos-transaction-fee-estimator.ts @@ -1,8 +1,6 @@ import type { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import type { EncodeObject } from "@cosmjs/proto-signing"; import type { Coin, MsgSendEncodeObject } from "@cosmjs/stargate"; -import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; -import { generateIbcTransferMessage } from "../helpers/cosmos-messages"; export class CosmosTransactionFeeEstimator { private static async estimateGasForTransaction< @@ -48,16 +46,4 @@ export class CosmosTransactionFeeEstimator { memo ); } - static estimateGasForIBCTransfer( - signingCosmWasmClient: SigningCosmWasmClient, - ibcTransferParams: MsgTransfer, - memo = "" - ): Promise { - return this.estimateGasForTransaction( - signingCosmWasmClient, - ibcTransferParams.sender, - [generateIbcTransferMessage(ibcTransferParams)], - memo - ); - } } diff --git a/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts b/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts deleted file mode 100644 index 406d8bd032a..00000000000 --- a/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { BridgeDataFetcher } from "../actions/ibc-transfer/services/bridge-data-fetcher"; -import axios from "axios"; - -vi.mock("axios"); - -describe("BridgeDataFetcher", () => { - let fetcher: BridgeDataFetcher; - - beforeEach(() => { - fetcher = BridgeDataFetcher.getInstance(); - vi.clearAllMocks(); - }); - - it("should return the same instance from getInstance", () => { - const fetcher1 = BridgeDataFetcher.getInstance(); - const fetcher2 = BridgeDataFetcher.getInstance(); - expect(fetcher1).toBe(fetcher2); - }); - - it("should use cache when data is already fetched", async () => { - const mockResponse = { - dest_assets: { - someKey: { - assets: [ - { - denom: "atom", - chain_id: "cosmos", - origin_denom: "atom", - origin_chain_id: "cosmos", - trace: "someTrace", - symbol: "ATOM", - name: "Cosmos Atom", - logo_uri: "http://someurl.com/logo.png", - decimals: 6, - recommended_symbol: "ATOM", - }, - ], - }, - }, - }; - - // @ts-expect-error -- ... - axios.post.mockResolvedValueOnce({ data: mockResponse }); - - const sourceAssetDenom = "atom"; - const sourceAssetChainId = "cosmos"; - - await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); - - expect(axios.post).toHaveBeenCalledTimes(1); - - await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); - expect(axios.post).toHaveBeenCalledTimes(1); // axios nie powinien być wywołany ponownie - }); - - it("should fetch and cache data correctly", async () => { - const mockResponse = { - dest_assets: { - someKey: { - assets: [ - { - denom: "atom", - chain_id: "cosmos", - origin_denom: "atom", - origin_chain_id: "cosmos", - trace: "someTrace", - symbol: "ATOM", - name: "Cosmos Atom", - logo_uri: "http://someurl.com/logo.png", - decimals: 6, - recommended_symbol: "ATOM", - }, - ], - }, - }, - }; - - // @ts-expect-error -- ... - axios.post.mockResolvedValueOnce({ data: mockResponse }); - - const sourceAssetDenom = "atom"; - const sourceAssetChainId = "cosmos"; - - const result = await fetcher.fetchBridgeData( - sourceAssetDenom, - sourceAssetChainId - ); - - expect(result).toEqual(mockResponse); - - const cacheKey = `${sourceAssetDenom}_${sourceAssetChainId}`; - expect(fetcher["cache"].has(cacheKey)).toBe(true); - - const cachedResult = await fetcher.fetchBridgeData( - sourceAssetDenom, - sourceAssetChainId - ); - expect(cachedResult).toEqual(mockResponse); - }); -}); diff --git a/packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts b/packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts deleted file mode 100644 index f2834ad09a1..00000000000 --- a/packages/plugin-cosmos/src/tests/bridge-data-provider.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { bridgeDataProvider } from "../actions/ibc-transfer/services/bridge-data-provider"; -import { BridgeDataFetcher } from "../actions/ibc-transfer/services/bridge-data-fetcher"; -import { vi, expect, it, beforeEach, describe } from "vitest"; - -vi.mock("./bridge-data-fetcher", () => ({ - BridgeDataFetcher: { - getInstance: vi.fn().mockReturnValue({ - fetchBridgeData: vi.fn(), - }), - }, -})); - -describe("bridgeDataProvider", () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let mockFetchBridgeData: any; - - beforeEach(() => { - mockFetchBridgeData = vi.fn(); - BridgeDataFetcher.getInstance().fetchBridgeData = mockFetchBridgeData; - }); - - it("should return correct channelId and ibcDenom when valid data is returned", async () => { - const mockResponse = { - dest_assets: { - cosmos: { - assets: [ - { - origin_denom: "atom", - denom: "uatom", - trace: "channel-123/abc", - }, - ], - }, - }, - }; - - mockFetchBridgeData.mockResolvedValue(mockResponse); - - const sourceAssetDenom = "atom"; - const sourceAssetChainId = "cosmos"; - - const result = await bridgeDataProvider( - sourceAssetDenom, - sourceAssetChainId - ); - - expect(result).toEqual({ - channelId: "channel-123", - ibcDenom: "uatom", - portId: "transfer", - }); - }); - - it("should throw an error when ibcAssetData is not found", async () => { - const mockResponse = { - dest_assets: { - cosmos: { - assets: [ - { - origin_denom: "btc", - denom: "ubtc", - trace: "channel-123/abc", - }, - ], - }, - }, - }; - - mockFetchBridgeData.mockResolvedValue(mockResponse); - - const sourceAssetDenom = "atom"; - const sourceAssetChainId = "cosmos"; - - await expect( - bridgeDataProvider(sourceAssetDenom, sourceAssetChainId) - ).rejects.toThrowError(); - }); - - it("should throw an error when channelId is missing", async () => { - const mockResponse = { - dest_assets: { - cosmos: { - assets: [ - { - origin_denom: "atom", - denom: "uatom", - trace: "", - }, - ], - }, - }, - }; - - mockFetchBridgeData.mockResolvedValue(mockResponse); - - const sourceAssetDenom = "atom"; - const sourceAssetChainId = "cosmos"; - - await expect( - bridgeDataProvider(sourceAssetDenom, sourceAssetChainId) - ).rejects.toThrowError(); - }); -}); diff --git a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts index fc7fbd5e6e0..1c1b3dbf7b3 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { CosmosIBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; +import { IBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; import { IBCTransferActionParams } from "../actions/ibc-transfer/types"; import { getAssetBySymbol, getChainByChainName } from "@chain-registry/utils"; @@ -31,7 +31,7 @@ vi.mock("../shared/helpers/cosmos-transaction-receipt.ts", () => ({ getPaidFeeFromReceipt: vi.fn().mockReturnValue("200000"), })); -describe("CosmosIBCTransferAction", () => { +describe("IBCTransferAction", () => { const mockSigningCosmWasmClient = { sendTokens: vi.fn().mockResolvedValue({ transactionHash: "mockTxHash", @@ -65,7 +65,7 @@ describe("CosmosIBCTransferAction", () => { portId: "transfer", }); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -100,7 +100,7 @@ describe("CosmosIBCTransferAction", () => { portId: "transfer", }); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -126,7 +126,7 @@ describe("CosmosIBCTransferAction", () => { portId: "transfer", }); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -153,7 +153,7 @@ describe("CosmosIBCTransferAction", () => { "cosmos1address" ); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -181,7 +181,7 @@ describe("CosmosIBCTransferAction", () => { "cosmos1address" ); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -209,7 +209,7 @@ describe("CosmosIBCTransferAction", () => { "cosmos1address" ); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -236,7 +236,7 @@ describe("CosmosIBCTransferAction", () => { "cosmos1address" ); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -266,7 +266,7 @@ describe("CosmosIBCTransferAction", () => { "cosmos1address" ); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); @@ -298,7 +298,7 @@ describe("CosmosIBCTransferAction", () => { "cosmos1address" ); - const cosmosIBCTransferAction = new CosmosIBCTransferAction( + const cosmosIBCTransferAction = new IBCTransferAction( mockCosmosWalletChains ); diff --git a/packages/plugin-cosmos/src/tests/cosmos-message.test.ts b/packages/plugin-cosmos/src/tests/cosmos-message.test.ts deleted file mode 100644 index 3a5a46c3d21..00000000000 --- a/packages/plugin-cosmos/src/tests/cosmos-message.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { generateIbcTransferMessage } from "../shared/helpers/cosmos-messages"; -import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; -import { MsgTransferEncodeObject } from "@cosmjs/stargate"; - -describe("generateIbcTransferMessage", () => { - it("should return a correctly formatted MsgTransferEncodeObject", () => { - const ibcTransferParams: MsgTransfer = { - sourcePort: "transfer", - sourceChannel: "channel-0", - token: { - denom: "uatom", - amount: "1000", - }, - sender: "cosmos1...", - receiver: "cosmos2...", - timeoutHeight: { - revisionHeight: BigInt(1000), - revisionNumber: BigInt(1), - }, - timeoutTimestamp: BigInt(1625140800), - memo: "", - }; - - const result: MsgTransferEncodeObject = - generateIbcTransferMessage(ibcTransferParams); - - expect(result).toEqual({ - typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - value: ibcTransferParams, - }); - }); -}); diff --git a/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts b/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts index ed10781ecfe..dd205c77d91 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-transaction-fee-estimator.test.ts @@ -1,8 +1,6 @@ import { describe, it, expect, vi, beforeEach, Mock } from "vitest"; import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { CosmosTransactionFeeEstimator } from "../shared/services/cosmos-transaction-fee-estimator"; -import { generateIbcTransferMessage } from "../shared/helpers/cosmos-messages"; -import { MsgTransfer } from "interchain/dist/codegen/ibc/applications/transfer/v1/tx"; vi.mock("@cosmjs/cosmwasm-stargate", () => ({ SigningCosmWasmClient: { @@ -97,100 +95,4 @@ describe("FeeEstimator", () => { "" ); }); - - it("should estimate gas for an IBC transfer successfully", async () => { - const mockGasEstimation = 300000; - - (mockSigningCosmWasmClient.simulate as Mock).mockResolvedValue( - mockGasEstimation - ); - - const ibcTransferParams: MsgTransfer = { - sourcePort: "transfer", - sourceChannel: "channel-0", - token: { denom: "uatom", amount: "1000000" }, - sender: "cosmos1senderaddress", - receiver: "cosmos1recipientaddress", - timeoutHeight: { - revisionNumber: BigInt(1), - revisionHeight: BigInt(1000), - }, - timeoutTimestamp: BigInt(0), - memo: "", - }; - - (generateIbcTransferMessage as Mock).mockReturnValue({ - typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - value: ibcTransferParams, - }); - - const memo = "IBC Test Memo"; - - const estimatedGas = - await CosmosTransactionFeeEstimator.estimateGasForIBCTransfer( - mockSigningCosmWasmClient, - ibcTransferParams, - memo - ); - - // Add 20% to the estimated gas to make sure we have enough gas to cover the transaction - expect(estimatedGas).toBe(mockGasEstimation + mockGasEstimation * 0.2); - - expect(mockSigningCosmWasmClient.simulate).toHaveBeenCalledWith( - ibcTransferParams.sender, - [ - { - typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - value: ibcTransferParams, - }, - ], - memo - ); - expect(generateIbcTransferMessage).toHaveBeenCalledWith( - ibcTransferParams - ); - }); - - it("should throw an error if gas estimation for IBC transfer fails", async () => { - (mockSigningCosmWasmClient.simulate as Mock).mockRejectedValue( - new Error("IBC gas estimation failed") - ); - - const ibcTransferParams: MsgTransfer = { - sourcePort: "transfer", - sourceChannel: "channel-0", - token: { denom: "uatom", amount: "1000000" }, - sender: "cosmos1senderaddress", - receiver: "cosmos1recipientaddress", - timeoutHeight: { - revisionNumber: BigInt(1), - revisionHeight: BigInt(1000), - }, - timeoutTimestamp: BigInt(0), - memo: "", - }; - - (generateIbcTransferMessage as Mock).mockReturnValue({ - typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - value: ibcTransferParams, - }); - - await expect( - CosmosTransactionFeeEstimator.estimateGasForIBCTransfer( - mockSigningCosmWasmClient, - ibcTransferParams - ) - ).rejects.toThrow("IBC gas estimation failed"); - - expect(mockSigningCosmWasmClient.simulate).toHaveBeenCalledWith( - ibcTransferParams.sender, - [ - { - typeUrl: "/ibc.applications.transfer.v1.MsgTransfer", - value: ibcTransferParams, - }, - ], - "" - ); - }); }); diff --git a/packages/plugin-cosmos/tsup.config.ts b/packages/plugin-cosmos/tsup.config.ts index 12d9ae64f96..90948913ae9 100644 --- a/packages/plugin-cosmos/tsup.config.ts +++ b/packages/plugin-cosmos/tsup.config.ts @@ -21,5 +21,6 @@ export default defineConfig({ "@cosmjs/proto-signing", "@cosmjs/cosmwasm-stargate", "zod", + "@ai16z/eliza", ], }); From b3d8b05f0edb65d82d89a80f4b72465fa72d2527 Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Thu, 9 Jan 2025 11:17:03 +0100 Subject: [PATCH 03/12] update: eliza package import --- .../src/actions/ibc-transfer/index.ts | 2 +- .../src/actions/transfer/index.ts | 2 +- packages/plugin-cosmos/src/index.ts | 2 +- .../src/providers/wallet/utils.ts | 2 +- pnpm-lock.yaml | 853 +++++++++++++++++- 5 files changed, 813 insertions(+), 48 deletions(-) diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts index 687d315f11f..6998112ac79 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -6,7 +6,7 @@ import { Memory, ModelClass, State, -} from "@ai16z/eliza"; +} from "@elizaos/core"; import { initWalletChainsData } from "../../providers/wallet/utils"; import { cosmosIBCTransferTemplate, diff --git a/packages/plugin-cosmos/src/actions/transfer/index.ts b/packages/plugin-cosmos/src/actions/transfer/index.ts index cf1049a4d62..efb9051b129 100644 --- a/packages/plugin-cosmos/src/actions/transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/transfer/index.ts @@ -6,7 +6,7 @@ import { Memory, ModelClass, State, -} from "@ai16z/eliza"; +} from "@elizaos/core"; import { initWalletChainsData } from "../../providers/wallet/utils"; import { cosmosTransferTemplate } from "../../templates"; import { CosmosTransferActionService } from "./services/cosmos-transfer-action-service"; diff --git a/packages/plugin-cosmos/src/index.ts b/packages/plugin-cosmos/src/index.ts index 8e3eeb9e276..ce2fff5c4b0 100644 --- a/packages/plugin-cosmos/src/index.ts +++ b/packages/plugin-cosmos/src/index.ts @@ -1,5 +1,5 @@ import { createTransferAction } from "./actions/transfer"; -import type { Plugin } from "@ai16z/eliza"; +import type { Plugin } from "@elizaos/core"; import { createCosmosWalletProvider } from "./providers/wallet"; import { ICosmosPluginOptions } from "./shared/interfaces"; diff --git a/packages/plugin-cosmos/src/providers/wallet/utils.ts b/packages/plugin-cosmos/src/providers/wallet/utils.ts index 9c3dac992cb..9163e16bb39 100644 --- a/packages/plugin-cosmos/src/providers/wallet/utils.ts +++ b/packages/plugin-cosmos/src/providers/wallet/utils.ts @@ -1,4 +1,4 @@ -import { IAgentRuntime } from "@ai16z/eliza"; +import { IAgentRuntime } from "@elizaos/core"; import { CosmosWalletChains } from "../../shared/entities/cosmos-wallet-chains-data"; export const initWalletChainsData = async (runtime: IAgentRuntime) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7e49154580d..6807d9057c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,7 +23,7 @@ importers: version: 3.9.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@vitest/eslint-plugin': specifier: 1.0.1 - version: 1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) + version: 1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0)) amqplib: specifier: 0.10.5 version: 0.10.5 @@ -48,7 +48,7 @@ importers: devDependencies: '@commitlint/cli': specifier: 18.6.1 - version: 18.6.1(@types/node@22.10.5)(typescript@5.6.3) + version: 18.6.1(@types/node@20.17.9)(typescript@5.6.3) '@commitlint/config-conventional': specifier: 18.6.3 version: 18.6.3 @@ -78,7 +78,7 @@ importers: version: 9.1.7 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.10.5) + version: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) lerna: specifier: 8.1.5 version: 8.1.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(encoding@0.1.13) @@ -90,7 +90,7 @@ importers: version: 3.4.1 ts-jest: specifier: ^29.1.1 - version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.10.5))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3) turbo: specifier: 2.3.3 version: 2.3.3 @@ -105,10 +105,10 @@ importers: version: 2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.24.1) vite: specifier: 5.4.11 - version: 5.4.11(@types/node@22.10.5)(terser@5.37.0) + version: 5.4.11(@types/node@20.17.9)(terser@5.37.0) vitest: specifier: 2.1.5 - version: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + version: 2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) agent: dependencies: @@ -1200,9 +1200,12 @@ importers: '@elizaos/core': specifier: workspace:* version: link:../core + '@skip-go/client': + specifier: ^0.16.3 + version: 0.16.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(starknet@6.18.0(encoding@0.1.13))(utf-8-validate@5.0.10)(viem@2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) axios: specifier: ^1.7.9 - version: 1.7.9(debug@4.4.0) + version: 1.7.9 bignumber.js: specifier: 9.1.2 version: 9.1.2 @@ -2492,6 +2495,24 @@ packages: resolution: {integrity: sha512-IQD9wkVReKAhsEAbDjh/0KrBGTEXelqZLpOBRDaIRvlzZ9sjmUP+gKbpvzyJnei2JHQiE8JAgj7YcNloINbGBw==} engines: {node: '>= 10'} + '@apollo/client@3.12.4': + resolution: {integrity: sha512-S/eC9jxEW9Jg1BjD6AZonE1fHxYuvC3gFHop8FRQkUdeK63MmBD5r0DOrN2WlJbwha1MSD6A97OwXwjaujEQpA==} + peerDependencies: + graphql: ^15.0.0 || ^16.0.0 + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + '@aptos-labs/aptos-cli@1.0.2': resolution: {integrity: sha512-PYPsd0Kk3ynkxNfe3S4fanI3DiUICCoh4ibQderbvjPFL5A0oK6F4lPEO2t0MDsQySTk2t4vh99Xjy6Bd9y+aQ==} hasBin: true @@ -3561,6 +3582,9 @@ packages: peerDependencies: '@solana/web3.js': ^1.68.0 + '@cosmjs/amino@0.31.3': + resolution: {integrity: sha512-36emtUq895sPRX8PTSOnG+lhJDCVyIcE0Tr5ct59sUbgQiI14y43vj/4WAlJ/utSOxy+Zhj9wxcs4AZfu0BHsw==} + '@cosmjs/amino@0.32.2': resolution: {integrity: sha512-lcK5RCVm4OfdAooxKcF2+NwaDVVpghOq6o/A40c2mHXDUzUoRZ33VAHjVJ9Me6vOFxshrw/XEFn1f4KObntjYA==} @@ -3570,42 +3594,72 @@ packages: '@cosmjs/cosmwasm-stargate@0.32.4': resolution: {integrity: sha512-Fuo9BGEiB+POJ5WeRyBGuhyKR1ordvxZGLPuPosFJOH9U0gKMgcjwKMCgAlWFkMlHaTB+tNdA8AifWiHrI7VgA==} + '@cosmjs/crypto@0.31.3': + resolution: {integrity: sha512-vRbvM9ZKR2017TO73dtJ50KxoGcFzKtKI7C8iO302BQ5p+DuB+AirUg1952UpSoLfv5ki9O416MFANNg8UN/EQ==} + '@cosmjs/crypto@0.32.4': resolution: {integrity: sha512-zicjGU051LF1V9v7bp8p7ovq+VyC91xlaHdsFOTo2oVry3KQikp8L/81RkXmUIT8FxMwdx1T7DmFwVQikcSDIw==} + '@cosmjs/encoding@0.31.3': + resolution: {integrity: sha512-6IRtG0fiVYwyP7n+8e54uTx2pLYijO48V3t9TLiROERm5aUAIzIlz6Wp0NYaI5he9nh1lcEGJ1lkquVKFw3sUg==} + '@cosmjs/encoding@0.32.4': resolution: {integrity: sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==} + '@cosmjs/json-rpc@0.31.3': + resolution: {integrity: sha512-7LVYerXjnm69qqYR3uA6LGCrBW2EO5/F7lfJxAmY+iII2C7xO3a0vAjMSt5zBBh29PXrJVS6c2qRP22W1Le2Wg==} + '@cosmjs/json-rpc@0.32.4': resolution: {integrity: sha512-/jt4mBl7nYzfJ2J/VJ+r19c92mUKF0Lt0JxM3MXEJl7wlwW5haHAWtzRujHkyYMXOwIR+gBqT2S0vntXVBRyhQ==} + '@cosmjs/math@0.31.3': + resolution: {integrity: sha512-kZ2C6glA5HDb9hLz1WrftAjqdTBb3fWQsRR+Us2HsjAYdeE6M3VdXMsYCP5M3yiihal1WDwAY2U7HmfJw7Uh4A==} + '@cosmjs/math@0.32.4': resolution: {integrity: sha512-++dqq2TJkoB8zsPVYCvrt88oJWsy1vMOuSOKcdlnXuOA/ASheTJuYy4+oZlTQ3Fr8eALDLGGPhJI02W2HyAQaw==} + '@cosmjs/proto-signing@0.31.3': + resolution: {integrity: sha512-24+10/cGl6lLS4VCrGTCJeDRPQTn1K5JfknzXzDIHOx8THR31JxA7/HV5eWGHqWgAbudA7ccdSvEK08lEHHtLA==} + '@cosmjs/proto-signing@0.32.2': resolution: {integrity: sha512-UV4WwkE3W3G3s7wwU9rizNcUEz2g0W8jQZS5J6/3fiN0mRPwtPKQ6EinPN9ASqcAJ7/VQH4/9EPOw7d6XQGnqw==} '@cosmjs/proto-signing@0.32.4': resolution: {integrity: sha512-QdyQDbezvdRI4xxSlyM1rSVBO2st5sqtbEIl3IX03uJ7YiZIQHyv6vaHVf1V4mapusCqguiHJzm4N4gsFdLBbQ==} + '@cosmjs/socket@0.31.3': + resolution: {integrity: sha512-aqrDGGi7os/hsz5p++avI4L0ZushJ+ItnzbqA7C6hamFSCJwgOkXaOUs+K9hXZdX4rhY7rXO4PH9IH8q09JkTw==} + '@cosmjs/socket@0.32.4': resolution: {integrity: sha512-davcyYziBhkzfXQTu1l5NrpDYv0K9GekZCC9apBRvL1dvMc9F/ygM7iemHjUA+z8tJkxKxrt/YPjJ6XNHzLrkw==} + '@cosmjs/stargate@0.31.3': + resolution: {integrity: sha512-53NxnzmB9FfXpG4KjOUAYAvWLYKdEmZKsutcat/u2BrDXNZ7BN8jim/ENcpwXfs9/Og0K24lEIdvA4gsq3JDQw==} + '@cosmjs/stargate@0.32.2': resolution: {integrity: sha512-AsJa29fT7Jd4xt9Ai+HMqhyj7UQu7fyYKdXj/8+/9PD74xe6lZSYhQPcitUmMLJ1ckKPgXSk5Dd2LbsQT0IhZg==} '@cosmjs/stargate@0.32.4': resolution: {integrity: sha512-usj08LxBSsPRq9sbpCeVdyLx2guEcOHfJS9mHGCLCXpdAPEIEQEtWLDpEUc0LEhWOx6+k/ChXTc5NpFkdrtGUQ==} + '@cosmjs/stream@0.31.3': + resolution: {integrity: sha512-8keYyI7X0RjsLyVcZuBeNjSv5FA4IHwbFKx7H60NHFXszN8/MvXL6aZbNIvxtcIHHsW7K9QSQos26eoEWlAd+w==} + '@cosmjs/stream@0.32.4': resolution: {integrity: sha512-Gih++NYHEiP+oyD4jNEUxU9antoC0pFSg+33Hpp0JlHwH0wXhtD3OOKnzSfDB7OIoEbrzLJUpEjOgpCp5Z+W3A==} + '@cosmjs/tendermint-rpc@0.31.3': + resolution: {integrity: sha512-s3TiWkPCW4QceTQjpYqn4xttUJH36mTPqplMl+qyocdqk5+X5mergzExU/pHZRWQ4pbby8bnR7kMvG4OC1aZ8g==} + '@cosmjs/tendermint-rpc@0.32.2': resolution: {integrity: sha512-DXyJHDmcAfCix4H/7/dKR0UMdshP01KxJOXHdHxBCbLIpck94BsWD3B2ZTXwfA6sv98so9wOzhp7qGQa5malxg==} '@cosmjs/tendermint-rpc@0.32.4': resolution: {integrity: sha512-MWvUUno+4bCb/LmlMIErLypXxy7ckUuzEmpufYYYd9wgbdCXaTaO08SZzyFM5PI8UJ/0S2AmUrgWhldlbxO8mw==} + '@cosmjs/utils@0.31.3': + resolution: {integrity: sha512-VBhAgzrrYdIe0O5IbKRqwszbQa7ZyQLx9nEQuHQ3HUplQW7P44COG/ye2n6AzCudtqxmwdX7nyX8ta1J07GoqA==} + '@cosmjs/utils@0.32.4': resolution: {integrity: sha512-D1Yc+Zy8oL/hkUkFUL/bwxvuDBzRGpc4cF7/SkdhxX4iHpSLgdOuTt1mhCh9+kl6NQREy9t7SYZ6xeW5gFe60w==} @@ -4217,6 +4271,12 @@ packages: '@emnapi/wasi-threads@1.0.1': resolution: {integrity: sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==} + '@ensdomains/ens-validation@0.1.0': + resolution: {integrity: sha512-rbDh2K6GfqXvBcJUISaTTYEt3f079WA4ohTE5Lh4/8EaaPAk/9vk3EisMUQV2UVxeFIZQEEyRCIOmRTpqN0W7A==} + + '@ensdomains/eth-ens-namehash@2.0.15': + resolution: {integrity: sha512-JRDFP6+Hczb1E0/HhIg0PONgBYasfGfDheujmfxaZaAv/NAH4jE6Kf48WbqfRZdxt4IZI3jl3Ri7sZ1nP09lgw==} + '@es-joy/jsdoccomment@0.41.0': resolution: {integrity: sha512-aKUhyn1QI5Ksbqcr3fFJj16p99QdjUxXAEuFst1Z47DRyoiMwivIH9MV/ARcJOCXVjPfjITciej8ZD2O/6qUmw==} engines: {node: '>=16'} @@ -5312,6 +5372,54 @@ packages: peerDependencies: google-protobuf: ^3.14.0 + '@injectivelabs/core-proto-ts@0.0.21': + resolution: {integrity: sha512-RBxSkRBCty60R/l55/D1jsSW0Aof5dyGFhCFdN3A010KjMv/SzZGGr+6DZPY/hflyFeaJzDv/VTopCymKNRBvQ==} + + '@injectivelabs/dmm-proto-ts@1.0.19': + resolution: {integrity: sha512-2FCzCziy1RhzmnkAVIU+Asby/GXAVQqKt5/o1s52j0LJXfJMpiCrV6soLfnjTebj61T+1WvJBPFoZCCiVYBpcw==} + + '@injectivelabs/exceptions@1.14.33': + resolution: {integrity: sha512-2c8YzLgwTOOsyc1WheqdM8jEfgGBhVrXN4cZ0jsikFVsLF619IDRn3hjIYqTeNERaEpeRPiuJGfZDu0DomwFrQ==} + + '@injectivelabs/grpc-web-node-http-transport@0.0.2': + resolution: {integrity: sha512-rpyhXLiGY/UMs6v6YmgWHJHiO9l0AgDyVNv+jcutNVt4tQrmNvnpvz2wCAGOFtq5LuX/E9ChtTVpk3gWGqXcGA==} + peerDependencies: + '@injectivelabs/grpc-web': '>=0.0.1' + + '@injectivelabs/grpc-web-react-native-transport@0.0.2': + resolution: {integrity: sha512-mk+aukQXnYNgPsPnu3KBi+FD0ZHQpazIlaBZ2jNZG7QAVmxTWtv3R66Zoq99Wx2dnE946NsZBYAoa0K5oSjnow==} + peerDependencies: + '@injectivelabs/grpc-web': '>=0.0.1' + + '@injectivelabs/grpc-web@0.0.1': + resolution: {integrity: sha512-Pu5YgaZp+OvR5UWfqbrPdHer3+gDf+b5fQoY+t2VZx1IAVHX8bzbN9EreYTvTYtFeDpYRWM8P7app2u4EX5wTw==} + peerDependencies: + google-protobuf: ^3.14.0 + + '@injectivelabs/indexer-proto-ts@1.11.32': + resolution: {integrity: sha512-gCkbMlBq34MY2xZcauDEsCP0h5l/FgKMwCgJ8aWGaTkh27XBWpl1zvlreuWg/IpSvTPJZBoADW9KqixqyoBdJw==} + + '@injectivelabs/mito-proto-ts@1.0.55': + resolution: {integrity: sha512-clFKpU/LCYvYiPg5PRjhVJFTxKcfJHzaj5saJHuL32LaOaB3Rd8L3CqP9qUrg78L7eKjjXjyG97U3NdRdZBlWg==} + + '@injectivelabs/networks@1.14.33': + resolution: {integrity: sha512-XDhAYwWYKdKBRfwO/MIfMyKjKRWz/AliMJG9yaM1C/cDlGHmA3EY7Au2Nf+PdkRhuxl2FzLV2Hp4uWeC0g8BYw==} + + '@injectivelabs/sdk-ts@1.14.5': + resolution: {integrity: sha512-j/6EcvNgQn563L0P5x80cZDTbYYbsXmHgtIbj8DCzemzgPRadmZLtlMDBjMQZ0ZcMhDSMfVOCINBOB2bBz2qMw==} + + '@injectivelabs/test-utils@1.14.33': + resolution: {integrity: sha512-1SfIRsMnWcJAYNrrpY+ZUWmbD62lWWdIvD6c+FYmFKS14zU3yDIK9NXe9g1lTM/GdUVkVKQgGg2QAYZ5f2G/xA==} + + '@injectivelabs/token-metadata@1.14.11': + resolution: {integrity: sha512-WKJlvjKiTRHxpOeez4kYrzIwgWmpspD1IMxWclkTysAcwGltUfUsvUhu1cKuACleIjFFWmiZv/HoGRFdvEAZ8w==} + + '@injectivelabs/ts-types@1.14.33': + resolution: {integrity: sha512-sJZzMNJtZFFZoPKZ91G09bxrZqQ5aS9omoTjQWy+7OxfiRAakzhsarTueX47hm6oTaN0XeBgD3wkMukkWUaobw==} + + '@injectivelabs/utils@1.14.33': + resolution: {integrity: sha512-zsezML4dTujF0xGLhcGmWBCghfJiy9MW+r6VqR8zJUlxnmnEdNpmsvBhBI6cmmov6Se4FL+yALAIFRvTm3txbg==} + '@ioredis/commands@1.2.0': resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} @@ -5431,6 +5539,14 @@ packages: '@jspm/core@2.1.0': resolution: {integrity: sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==} + '@keplr-wallet/types@0.12.172': + resolution: {integrity: sha512-SfsUxSEJqVcAhpy0HJFNBxF/4mSCNZy3GxcoYVY+WKSfGMMabp5PwyKKJxKuiNc9Ar752+60l1PQkYy5zYyKOA==} + peerDependencies: + starknet: ^6 + + '@keplr-wallet/unit@0.12.172': + resolution: {integrity: sha512-kMcPgysxy7nS9PtHAYxrBZnweSoT/ifbRAnfdJT2RG+rIvHEsgU0odf3RVu+0eG7XaKeV6+mDkrE59aBvyFTyQ==} + '@kikobeats/time-span@1.0.5': resolution: {integrity: sha512-txRAdmi35N1wnsLS1AO5mTlbY5Cv5/61WXqek2y3L9Q7u4mgdUVq819so5xe753hL5gYeLzlWoJ/VJfXg9nx8g==} engines: {node: '>= 18'} @@ -7672,6 +7788,12 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@skip-go/client@0.16.3': + resolution: {integrity: sha512-ucg4WVXRENCOp/i5Sb5CAID6A8UOJRZs9A1lJR4vnYREJUESA8JnS7bh8UQKsREEHTjMYKV4O90EV2W/XCKoFw==} + peerDependencies: + '@solana/web3.js': ^1.95.8 + viem: 2.21.58 + '@slack/events-api@3.0.1': resolution: {integrity: sha512-ReJzZRpCgwGtKrAT0tRMppO3zm72jmxsOlTgR7PGajv2oq/tOJSeVRm7RcGiwn3EPIuovKkD/mr4TTN4n801fQ==} engines: {node: '>=12.13.0', npm: '>=6.12.0'} @@ -8719,6 +8841,9 @@ packages: '@types/lodash.isstring@4.0.9': resolution: {integrity: sha512-sjGPpa15VBpMns/4s6Blm567JgxLVVu/eCYCe7h/TdQyPCz9lIhaLSISjN7ZC9cDXmUT2IM/4mNRw8OtYirziw==} + '@types/lodash.values@4.3.9': + resolution: {integrity: sha512-IJ20OEfqNwm3k8ENwoM3q0yOs4UMpgtD4GqxB4lwBHToGthHWqhyh5DdSgQjioocz0QK2SSBkJfCq95ZTV8BTw==} + '@types/lodash@4.17.14': resolution: {integrity: sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==} @@ -9329,6 +9454,22 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@wry/caches@1.0.1': + resolution: {integrity: sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==} + engines: {node: '>=8'} + + '@wry/context@0.7.4': + resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} + engines: {node: '>=8'} + + '@wry/equality@0.5.7': + resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} + engines: {node: '>=8'} + + '@wry/trie@0.5.0': + resolution: {integrity: sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==} + engines: {node: '>=8'} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -9611,6 +9752,10 @@ packages: resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} engines: {node: '>=0.10.0'} + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -10433,6 +10578,10 @@ packages: resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} engines: {node: '>=0.10.0'} + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -10689,10 +10838,16 @@ packages: collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -10953,6 +11108,10 @@ packages: peerDependencies: webpack: ^5.1.0 + copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + core-js-compat@3.39.0: resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==} @@ -11007,6 +11166,12 @@ packages: typescript: optional: true + cosmjs-types@0.7.2: + resolution: {integrity: sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==} + + cosmjs-types@0.8.0: + resolution: {integrity: sha512-Q2Mj95Fl0PYMWEhA2LuGEIhipF7mQwd9gTQ85DdP9jjjopeoGaDxvmPa5nakNzsq7FnO1DMTatXTAx6bxMH7Lg==} + cosmjs-types@0.9.0: resolution: {integrity: sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==} @@ -12323,6 +12488,10 @@ packages: ethereumjs-util@6.2.1: resolution: {integrity: sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==} + ethereumjs-util@7.1.5: + resolution: {integrity: sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==} + engines: {node: '>=10.0.0'} + ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} @@ -13444,6 +13613,9 @@ packages: resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} engines: {node: '>=0.8', npm: '>=1.3.7'} + http-status-codes@2.3.0: + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + http2-wrapper@1.0.3: resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} engines: {node: '>=10.19.0'} @@ -14305,6 +14477,10 @@ packages: jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jscrypto@1.0.3: + resolution: {integrity: sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ==} + hasBin: true + jsdoc-type-pratt-parser@4.0.0: resolution: {integrity: sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==} engines: {node: '>=12.0.0'} @@ -14407,6 +14583,9 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} + jsonschema@1.5.0: + resolution: {integrity: sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==} + jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} @@ -14690,6 +14869,11 @@ packages: resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + link-module-alias@1.2.0: + resolution: {integrity: sha512-ahPjXepbSVKbahTB6LxR//VHm8HPfI+QQygCH+E82spBY4HR5VPJTvlhKBc9F7muVxnS6C1rRfoPOXAbWO/fyw==} + engines: {node: '> 8.0.0'} + hasBin: true + linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -14851,6 +15035,9 @@ packages: lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + lodash.values@4.3.0: + resolution: {integrity: sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -15797,6 +15984,9 @@ packages: engines: {node: '>=10'} hasBin: true + noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + nopt@1.0.10: resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} hasBin: true @@ -16074,6 +16264,9 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + optimism@0.18.1: + resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==} + optional@0.1.4: resolution: {integrity: sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==} @@ -17938,6 +18131,17 @@ packages: resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true + rehackt@0.1.0: + resolution: {integrity: sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==} + peerDependencies: + '@types/react': '*' + react: '*' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + rehype-parse@7.0.1: resolution: {integrity: sha512-fOiR9a9xH+Le19i4fGzIEowAbwG7idy2Jzs4mOrFWBSJ0sNUgy0ev871dwWnbOo371SjgjG4pwzrbgSVrKxecw==} @@ -18046,6 +18250,10 @@ packages: engines: {node: '>= 0.4'} hasBin: true + response-iterator@0.2.11: + resolution: {integrity: sha512-5tdhcAeGMSyM0/FoxAYjoOxQZ2tRR2H/S/t6kGRXu6iiWcGY5UnZgkVANbTwBVUSGqWu0ADctmoi6lOCIF8uKQ==} + engines: {node: '>=0.8'} + responselike@2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} @@ -18414,6 +18622,11 @@ packages: shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + shx@0.3.4: + resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} + engines: {node: '>=6'} + hasBin: true + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -18520,6 +18733,10 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + snakecase-keys@5.5.0: + resolution: {integrity: sha512-r3kRtnoPu3FxGJ3fny6PKNnU3pteb29o6qAa0ugzhSseKNWRkw1dw8nIjXMyyKaU9vQxxVIE62Mb3bKbdrgpiw==} + engines: {node: '>=12'} + sockjs@0.3.24: resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} @@ -18729,6 +18946,9 @@ packages: resolution: {integrity: sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==} engines: {node: '>=18'} + store2@2.14.4: + resolution: {integrity: sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==} + stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} @@ -18958,6 +19178,10 @@ packages: resolution: {integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==} engines: {node: '>=0.10'} + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -19306,6 +19530,10 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-invariant@0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + ts-jest@29.2.5: resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -19518,6 +19746,10 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type-fest@3.13.1: + resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} + engines: {node: '>=14.16'} + type-fest@4.31.0: resolution: {integrity: sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==} engines: {node: '>=16'} @@ -19872,6 +20104,10 @@ packages: uploadthing: optional: true + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + untyped@1.5.2: resolution: {integrity: sha512-eL/8PlhLcMmlMDtNPKhyyz9kEBDS3Uk4yMu/ewlkT2WFbtzScjHWPJLdQLmaGPUKjXzwe9MumOtOgc4Fro96Kg==} hasBin: true @@ -20777,6 +21013,12 @@ packages: yup@1.6.1: resolution: {integrity: sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==} + zen-observable-ts@1.2.5: + resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} + + zen-observable@0.8.15: + resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} + zimmerframe@1.1.2: resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} @@ -20830,7 +21072,7 @@ snapshots: '@acuminous/bitsyntax@0.1.2': dependencies: buffer-more-ints: 1.0.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 safe-buffer: 5.1.2 transitivePeerDependencies: - supports-color @@ -21253,6 +21495,29 @@ snapshots: '@anush008/tokenizers-linux-x64-gnu': 0.0.0 '@anush008/tokenizers-win32-x64-msvc': 0.0.0 + '@apollo/client@3.12.4(@types/react@18.3.12)(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) + '@wry/caches': 1.0.1 + '@wry/equality': 0.5.7 + '@wry/trie': 0.5.0 + graphql: 16.10.0 + graphql-tag: 2.12.6(graphql@16.10.0) + hoist-non-react-statics: 3.3.2 + optimism: 0.18.1 + prop-types: 15.8.1 + rehackt: 0.1.0(@types/react@18.3.12)(react@18.3.1) + response-iterator: 0.2.11 + symbol-observable: 4.0.0 + ts-invariant: 0.10.3 + tslib: 2.8.1 + zen-observable-ts: 1.2.5 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + '@aptos-labs/aptos-cli@1.0.2': dependencies: commander: 12.1.0 @@ -21970,7 +22235,7 @@ snapshots: '@babel/traverse': 7.26.4 '@babel/types': 7.26.3 convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -22760,7 +23025,7 @@ snapshots: '@babel/parser': 7.26.3 '@babel/template': 7.25.9 '@babel/types': 7.26.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -22908,7 +23173,7 @@ snapshots: dependencies: '@scure/bip32': 1.6.1 abitype: 1.0.8(typescript@5.6.3)(zod@3.24.1) - axios: 1.7.9(debug@4.4.0) + axios: 1.7.9 axios-mock-adapter: 1.22.0(axios@1.7.9) axios-retry: 4.5.0(axios@1.7.9) bip32: 4.0.0 @@ -22929,11 +23194,11 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@commitlint/cli@18.6.1(@types/node@22.10.5)(typescript@5.6.3)': + '@commitlint/cli@18.6.1(@types/node@20.17.9)(typescript@5.6.3)': dependencies: '@commitlint/format': 18.6.1 '@commitlint/lint': 18.6.1 - '@commitlint/load': 18.6.1(@types/node@22.10.5)(typescript@5.6.3) + '@commitlint/load': 18.6.1(@types/node@20.17.9)(typescript@5.6.3) '@commitlint/read': 18.6.1 '@commitlint/types': 18.6.1 execa: 5.1.1 @@ -22983,7 +23248,7 @@ snapshots: '@commitlint/rules': 18.6.1 '@commitlint/types': 18.6.1 - '@commitlint/load@18.6.1(@types/node@22.10.5)(typescript@5.6.3)': + '@commitlint/load@18.6.1(@types/node@20.17.9)(typescript@5.6.3)': dependencies: '@commitlint/config-validator': 18.6.1 '@commitlint/execute-rule': 18.6.1 @@ -22991,7 +23256,7 @@ snapshots: '@commitlint/types': 18.6.1 chalk: 4.1.2 cosmiconfig: 8.3.6(typescript@5.6.3) - cosmiconfig-typescript-loader: 5.1.0(@types/node@22.10.5)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3) + cosmiconfig-typescript-loader: 5.1.0(@types/node@20.17.9)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -23166,6 +23431,13 @@ snapshots: bn.js: 5.2.1 buffer-layout: 1.2.2 + '@cosmjs/amino@0.31.3': + dependencies: + '@cosmjs/crypto': 0.31.3 + '@cosmjs/encoding': 0.31.3 + '@cosmjs/math': 0.31.3 + '@cosmjs/utils': 0.31.3 + '@cosmjs/amino@0.32.2': dependencies: '@cosmjs/crypto': 0.32.4 @@ -23197,6 +23469,16 @@ snapshots: - debug - utf-8-validate + '@cosmjs/crypto@0.31.3': + dependencies: + '@cosmjs/encoding': 0.31.3 + '@cosmjs/math': 0.31.3 + '@cosmjs/utils': 0.31.3 + '@noble/hashes': 1.7.0 + bn.js: 5.2.1 + elliptic: 6.6.1 + libsodium-wrappers-sumo: 0.7.15 + '@cosmjs/crypto@0.32.4': dependencies: '@cosmjs/encoding': 0.32.4 @@ -23207,21 +23489,46 @@ snapshots: elliptic: 6.6.1 libsodium-wrappers-sumo: 0.7.15 + '@cosmjs/encoding@0.31.3': + dependencies: + base64-js: 1.5.1 + bech32: 1.1.4 + readonly-date: 1.0.0 + '@cosmjs/encoding@0.32.4': dependencies: base64-js: 1.5.1 bech32: 1.1.4 readonly-date: 1.0.0 + '@cosmjs/json-rpc@0.31.3': + dependencies: + '@cosmjs/stream': 0.31.3 + xstream: 11.14.0 + '@cosmjs/json-rpc@0.32.4': dependencies: '@cosmjs/stream': 0.32.4 xstream: 11.14.0 + '@cosmjs/math@0.31.3': + dependencies: + bn.js: 5.2.1 + '@cosmjs/math@0.32.4': dependencies: bn.js: 5.2.1 + '@cosmjs/proto-signing@0.31.3': + dependencies: + '@cosmjs/amino': 0.31.3 + '@cosmjs/crypto': 0.31.3 + '@cosmjs/encoding': 0.31.3 + '@cosmjs/math': 0.31.3 + '@cosmjs/utils': 0.31.3 + cosmjs-types: 0.8.0 + long: 4.0.0 + '@cosmjs/proto-signing@0.32.2': dependencies: '@cosmjs/amino': 0.32.4 @@ -23240,6 +23547,16 @@ snapshots: '@cosmjs/utils': 0.32.4 cosmjs-types: 0.9.0 + '@cosmjs/socket@0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@cosmjs/stream': 0.31.3 + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@cosmjs/socket@0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@cosmjs/stream': 0.32.4 @@ -23250,6 +23567,25 @@ snapshots: - bufferutil - utf-8-validate + '@cosmjs/stargate@0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@confio/ics23': 0.6.8 + '@cosmjs/amino': 0.31.3 + '@cosmjs/encoding': 0.31.3 + '@cosmjs/math': 0.31.3 + '@cosmjs/proto-signing': 0.31.3 + '@cosmjs/stream': 0.31.3 + '@cosmjs/tendermint-rpc': 0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/utils': 0.31.3 + cosmjs-types: 0.8.0 + long: 4.0.0 + protobufjs: 6.11.4 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/stargate@0.32.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@confio/ics23': 0.6.8 @@ -23284,10 +23620,31 @@ snapshots: - debug - utf-8-validate + '@cosmjs/stream@0.31.3': + dependencies: + xstream: 11.14.0 + '@cosmjs/stream@0.32.4': dependencies: xstream: 11.14.0 + '@cosmjs/tendermint-rpc@0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@cosmjs/crypto': 0.31.3 + '@cosmjs/encoding': 0.31.3 + '@cosmjs/json-rpc': 0.31.3 + '@cosmjs/math': 0.31.3 + '@cosmjs/socket': 0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/stream': 0.31.3 + '@cosmjs/utils': 0.31.3 + axios: 0.21.4 + readonly-date: 1.0.0 + xstream: 11.14.0 + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@cosmjs/tendermint-rpc@0.32.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@cosmjs/crypto': 0.32.4 @@ -23297,7 +23654,7 @@ snapshots: '@cosmjs/socket': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@cosmjs/stream': 0.32.4 '@cosmjs/utils': 0.32.4 - axios: 1.7.9(debug@4.4.0) + axios: 1.7.9 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -23314,7 +23671,7 @@ snapshots: '@cosmjs/socket': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@cosmjs/stream': 0.32.4 '@cosmjs/utils': 0.32.4 - axios: 1.7.9(debug@4.4.0) + axios: 1.7.9 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -23322,6 +23679,8 @@ snapshots: - debug - utf-8-validate + '@cosmjs/utils@0.31.3': {} + '@cosmjs/utils@0.32.4': {} '@cosmology/lcd@0.13.5': @@ -24618,6 +24977,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@ensdomains/ens-validation@0.1.0': {} + + '@ensdomains/eth-ens-namehash@2.0.15': {} + '@es-joy/jsdoccomment@0.41.0': dependencies: comment-parser: 1.4.1 @@ -24929,7 +25292,7 @@ snapshots: '@eslint/config-array@0.19.1': dependencies: '@eslint/object-schema': 2.1.5 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -24955,7 +25318,7 @@ snapshots: '@eslint/eslintrc@3.2.0': dependencies: ajv: 6.12.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 @@ -25712,6 +26075,165 @@ snapshots: browser-headers: 0.4.1 google-protobuf: 3.21.4 + '@injectivelabs/core-proto-ts@0.0.21': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + google-protobuf: 3.21.4 + protobufjs: 7.4.0 + rxjs: 7.8.1 + + '@injectivelabs/dmm-proto-ts@1.0.19': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + google-protobuf: 3.21.4 + protobufjs: 7.4.0 + rxjs: 7.8.1 + + '@injectivelabs/exceptions@1.14.33(google-protobuf@3.21.4)': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + '@injectivelabs/ts-types': 1.14.33 + http-status-codes: 2.3.0 + shx: 0.3.4 + transitivePeerDependencies: + - google-protobuf + + '@injectivelabs/grpc-web-node-http-transport@0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4))': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + + '@injectivelabs/grpc-web-react-native-transport@0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4))': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + + '@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)': + dependencies: + browser-headers: 0.4.1 + google-protobuf: 3.21.4 + + '@injectivelabs/indexer-proto-ts@1.11.32': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + google-protobuf: 3.21.4 + protobufjs: 7.4.0 + rxjs: 7.8.1 + + '@injectivelabs/mito-proto-ts@1.0.55': + dependencies: + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + google-protobuf: 3.21.4 + protobufjs: 7.4.0 + rxjs: 7.8.1 + + '@injectivelabs/networks@1.14.33(google-protobuf@3.21.4)': + dependencies: + '@injectivelabs/exceptions': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/ts-types': 1.14.33 + '@injectivelabs/utils': 1.14.33(google-protobuf@3.21.4) + shx: 0.3.4 + transitivePeerDependencies: + - debug + - google-protobuf + + '@injectivelabs/sdk-ts@1.14.5(@types/react@18.3.12)(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10)': + dependencies: + '@apollo/client': 3.12.4(@types/react@18.3.12)(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@cosmjs/amino': 0.31.3 + '@cosmjs/proto-signing': 0.31.3 + '@cosmjs/stargate': 0.31.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@ensdomains/ens-validation': 0.1.0 + '@ensdomains/eth-ens-namehash': 2.0.15 + '@ethersproject/bytes': 5.7.0 + '@injectivelabs/core-proto-ts': 0.0.21 + '@injectivelabs/dmm-proto-ts': 1.0.19 + '@injectivelabs/exceptions': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/grpc-web': 0.0.1(google-protobuf@3.21.4) + '@injectivelabs/grpc-web-node-http-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)) + '@injectivelabs/grpc-web-react-native-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1(google-protobuf@3.21.4)) + '@injectivelabs/indexer-proto-ts': 1.11.32 + '@injectivelabs/mito-proto-ts': 1.0.55 + '@injectivelabs/networks': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/test-utils': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/token-metadata': 1.14.11(google-protobuf@3.21.4) + '@injectivelabs/ts-types': 1.14.33 + '@injectivelabs/utils': 1.14.33(google-protobuf@3.21.4) + '@metamask/eth-sig-util': 4.0.1 + axios: 0.27.2 + bech32: 2.0.0 + bip39: 3.1.0 + cosmjs-types: 0.7.2 + ethereumjs-util: 7.1.5 + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + google-protobuf: 3.21.4 + graphql: 16.10.0 + http-status-codes: 2.3.0 + js-sha3: 0.8.0 + jscrypto: 1.0.3 + keccak256: 1.0.6 + link-module-alias: 1.2.0 + secp256k1: 4.0.4 + shx: 0.3.4 + snakecase-keys: 5.5.0 + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - graphql-ws + - react + - react-dom + - subscriptions-transport-ws + - utf-8-validate + + '@injectivelabs/test-utils@1.14.33(google-protobuf@3.21.4)': + dependencies: + '@injectivelabs/exceptions': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/networks': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/ts-types': 1.14.33 + '@injectivelabs/utils': 1.14.33(google-protobuf@3.21.4) + axios: 1.7.9 + bignumber.js: 9.1.2 + shx: 0.3.4 + snakecase-keys: 5.5.0 + store2: 2.14.4 + transitivePeerDependencies: + - debug + - google-protobuf + + '@injectivelabs/token-metadata@1.14.11(google-protobuf@3.21.4)': + dependencies: + '@injectivelabs/exceptions': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/networks': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/ts-types': 1.14.33 + '@injectivelabs/utils': 1.14.33(google-protobuf@3.21.4) + '@types/lodash.values': 4.3.9 + copyfiles: 2.4.1 + jsonschema: 1.5.0 + link-module-alias: 1.2.0 + lodash: 4.17.21 + lodash.values: 4.3.0 + shx: 0.3.4 + transitivePeerDependencies: + - debug + - google-protobuf + + '@injectivelabs/ts-types@1.14.33': + dependencies: + shx: 0.3.4 + + '@injectivelabs/utils@1.14.33(google-protobuf@3.21.4)': + dependencies: + '@injectivelabs/exceptions': 1.14.33(google-protobuf@3.21.4) + '@injectivelabs/ts-types': 1.14.33 + axios: 1.7.9 + bignumber.js: 9.1.2 + http-status-codes: 2.3.0 + shx: 0.3.4 + snakecase-keys: 5.5.0 + store2: 2.14.4 + transitivePeerDependencies: + - debug + - google-protobuf + '@ioredis/commands@1.2.0': {} '@isaacs/cliui@8.0.2': @@ -26002,6 +26524,19 @@ snapshots: '@jspm/core@2.1.0': {} + '@keplr-wallet/types@0.12.172(starknet@6.18.0(encoding@0.1.13))': + dependencies: + long: 4.0.0 + starknet: 6.18.0(encoding@0.1.13) + + '@keplr-wallet/unit@0.12.172(starknet@6.18.0(encoding@0.1.13))': + dependencies: + '@keplr-wallet/types': 0.12.172(starknet@6.18.0(encoding@0.1.13)) + big-integer: 1.6.52 + utility-types: 3.11.0 + transitivePeerDependencies: + - starknet + '@kikobeats/time-span@1.0.5': {} '@kwsites/file-exists@1.1.1': @@ -29484,6 +30019,36 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@skip-go/client@0.16.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(starknet@6.18.0(encoding@0.1.13))(utf-8-validate@5.0.10)(viem@2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))': + dependencies: + '@cosmjs/amino': 0.32.4 + '@cosmjs/cosmwasm-stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/encoding': 0.32.4 + '@cosmjs/math': 0.32.4 + '@cosmjs/proto-signing': 0.32.4 + '@cosmjs/stargate': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@cosmjs/tendermint-rpc': 0.32.4(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@injectivelabs/core-proto-ts': 0.0.21 + '@injectivelabs/sdk-ts': 1.14.5(@types/react@18.3.12)(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) + '@keplr-wallet/unit': 0.12.172(starknet@6.18.0(encoding@0.1.13)) + '@solana/wallet-adapter-base': 0.9.23(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + axios: 1.7.9 + cosmjs-types: 0.9.0 + create-hash: 1.2.0 + keccak: 3.0.4 + viem: 2.21.58(bufferutil@4.0.9)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + transitivePeerDependencies: + - '@types/react' + - bufferutil + - debug + - graphql-ws + - react + - react-dom + - starknet + - subscriptions-transport-ws + - utf-8-validate + '@slack/events-api@3.0.1': dependencies: '@types/debug': 4.1.12 @@ -31227,6 +31792,10 @@ snapshots: dependencies: '@types/lodash': 4.17.14 + '@types/lodash.values@4.3.9': + dependencies: + '@types/lodash': 4.17.14 + '@types/lodash@4.17.14': {} '@types/long@4.0.2': {} @@ -31518,7 +32087,7 @@ snapshots: '@typescript-eslint/types': 8.16.0 '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) '@typescript-eslint/visitor-keys': 8.16.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 eslint: 9.16.0(jiti@2.4.2) optionalDependencies: typescript: 5.6.3 @@ -31564,7 +32133,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3) - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 eslint: 9.16.0(jiti@2.4.2) ts-api-utils: 1.4.3(typescript@5.6.3) optionalDependencies: @@ -31607,7 +32176,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.16.0 '@typescript-eslint/visitor-keys': 8.16.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -31721,13 +32290,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': + '@vitest/eslint-plugin@1.0.1(@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3))(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3)(vitest@2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0))': dependencies: eslint: 9.16.0(jiti@2.4.2) optionalDependencies: '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@2.4.2))(typescript@5.6.3) typescript: 5.6.3 - vitest: 2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) + vitest: 2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0) '@vitest/expect@1.2.1': dependencies: @@ -31757,6 +32326,14 @@ snapshots: optionalDependencies: vite: 5.4.11(@types/node@22.10.5)(terser@5.37.0) + '@vitest/mocker@2.1.5(vite@5.4.11(@types/node@20.17.9)(terser@5.37.0))': + dependencies: + '@vitest/spy': 2.1.5 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.11(@types/node@20.17.9)(terser@5.37.0) + '@vitest/mocker@2.1.5(vite@5.4.11(@types/node@22.10.5)(terser@5.37.0))': dependencies: '@vitest/spy': 2.1.5 @@ -32334,6 +32911,22 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 + '@wry/caches@1.0.1': + dependencies: + tslib: 2.8.1 + + '@wry/context@0.7.4': + dependencies: + tslib: 2.8.1 + + '@wry/equality@0.5.7': + dependencies: + tslib: 2.8.1 + + '@wry/trie@0.5.0': + dependencies: + tslib: 2.8.1 + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -32448,7 +33041,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -32640,6 +33233,10 @@ snapshots: ansi-styles@2.2.1: {} + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 @@ -32879,7 +33476,7 @@ snapshots: axios-mock-adapter@1.22.0(axios@1.7.9): dependencies: - axios: 1.7.9(debug@4.4.0) + axios: 1.7.9 fast-deep-equal: 3.1.3 is-buffer: 2.0.5 @@ -32890,18 +33487,18 @@ snapshots: axios-retry@4.5.0(axios@1.7.9): dependencies: - axios: 1.7.9(debug@4.4.0) + axios: 1.7.9 is-retry-allowed: 2.2.0 axios@0.21.4: dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 transitivePeerDependencies: - debug axios@0.27.2: dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 form-data: 4.0.1 transitivePeerDependencies: - debug @@ -32916,7 +33513,7 @@ snapshots: axios@1.7.4: dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 form-data: 4.0.1 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -32938,6 +33535,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.7.9: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axios@1.7.9(debug@4.4.0): dependencies: follow-redirects: 1.15.9(debug@4.4.0) @@ -33776,6 +34381,12 @@ snapshots: strip-ansi: 3.0.1 supports-color: 2.0.0 + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -34064,10 +34675,16 @@ snapshots: collect-v8-coverage@1.0.2: {} + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} color-string@1.9.1: @@ -34338,6 +34955,16 @@ snapshots: serialize-javascript: 6.0.2 webpack: 5.97.1(@swc/core@1.10.4(@swc/helpers@0.5.15)) + copyfiles@2.4.1: + dependencies: + glob: 7.2.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + noms: 0.0.0 + through2: 2.0.5 + untildify: 4.0.0 + yargs: 16.2.0 + core-js-compat@3.39.0: dependencies: browserslist: 4.24.3 @@ -34365,9 +34992,9 @@ snapshots: dependencies: layout-base: 2.0.1 - cosmiconfig-typescript-loader@5.1.0(@types/node@22.10.5)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3): + cosmiconfig-typescript-loader@5.1.0(@types/node@20.17.9)(cosmiconfig@8.3.6(typescript@5.6.3))(typescript@5.6.3): dependencies: - '@types/node': 22.10.5 + '@types/node': 20.17.9 cosmiconfig: 8.3.6(typescript@5.6.3) jiti: 1.21.7 typescript: 5.6.3 @@ -34396,6 +35023,16 @@ snapshots: optionalDependencies: typescript: 5.6.3 + cosmjs-types@0.7.2: + dependencies: + long: 4.0.0 + protobufjs: 6.11.4 + + cosmjs-types@0.8.0: + dependencies: + long: 4.0.0 + protobufjs: 6.11.4 + cosmjs-types@0.9.0: {} crc-32@1.2.2: {} @@ -35011,6 +35648,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.0: + dependencies: + ms: 2.1.3 + debug@4.4.0(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -35973,7 +36614,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 escape-string-regexp: 4.0.0 eslint-scope: 8.2.0 eslint-visitor-keys: 4.2.0 @@ -36190,6 +36831,14 @@ snapshots: ethjs-util: 0.1.6 rlp: 2.2.7 + ethereumjs-util@7.1.5: + dependencies: + '@types/bn.js': 5.1.6 + bn.js: 5.2.1 + create-hash: 1.2.0 + ethereum-cryptography: 0.1.3 + rlp: 2.2.7 + ethers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 @@ -36662,6 +37311,8 @@ snapshots: async: 0.2.10 which: 1.3.1 + follow-redirects@1.15.9: {} + follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: debug: 4.3.7 @@ -37783,7 +38434,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -37817,6 +38468,8 @@ snapshots: jsprim: 1.4.2 sshpk: 1.18.0 + http-status-codes@2.3.0: {} + http2-wrapper@1.0.3: dependencies: quick-lru: 5.1.1 @@ -37839,14 +38492,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -38431,7 +39084,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -39115,6 +39768,8 @@ snapshots: jsbn@1.1.0: {} + jscrypto@1.0.3: {} + jsdoc-type-pratt-parser@4.0.0: {} jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10): @@ -39219,6 +39874,8 @@ snapshots: jsonpointer@5.0.1: {} + jsonschema@1.5.0: {} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 @@ -39575,6 +40232,10 @@ snapshots: lines-and-columns@2.0.3: {} + link-module-alias@1.2.0: + dependencies: + chalk: 2.4.2 + linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -39740,6 +40401,8 @@ snapshots: lodash.upperfirst@4.3.1: {} + lodash.values@4.3.0: {} + lodash@4.17.21: {} log-symbols@4.1.0: @@ -39924,7 +40587,7 @@ snapshots: md5.js@1.3.5: dependencies: - hash-base: 3.0.5 + hash-base: 3.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 @@ -41140,6 +41803,11 @@ snapshots: touch: 3.1.1 undefsafe: 2.0.5 + noms@0.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 1.0.34 + nopt@1.0.10: dependencies: abbrev: 1.1.1 @@ -41276,7 +41944,7 @@ snapshots: '@yarnpkg/lockfile': 1.1.0 '@yarnpkg/parsers': 3.0.0-rc.46 '@zkochan/js-yaml': 0.0.7 - axios: 1.7.9(debug@4.4.0) + axios: 1.7.9 chalk: 4.1.0 cli-cursor: 3.1.0 cli-spinners: 2.6.1 @@ -41550,6 +42218,13 @@ snapshots: opener@1.5.2: {} + optimism@0.18.1: + dependencies: + '@wry/caches': 1.0.1 + '@wry/context': 0.7.4 + '@wry/trie': 0.5.0 + tslib: 2.8.1 + optional@0.1.4: {} optionator@0.9.4: @@ -43664,6 +44339,11 @@ snapshots: dependencies: jsesc: 3.0.2 + rehackt@0.1.0(@types/react@18.3.12)(react@18.3.1): + optionalDependencies: + '@types/react': 18.3.12 + react: 18.3.1 + rehype-parse@7.0.1: dependencies: hast-util-from-parse5: 6.0.1 @@ -43833,6 +44513,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + response-iterator@0.2.11: {} + responselike@2.0.1: dependencies: lowercase-keys: 2.0.0 @@ -44305,6 +44987,11 @@ snapshots: shimmer@1.2.1: {} + shx@0.3.4: + dependencies: + minimist: 1.2.8 + shelljs: 0.8.5 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -44443,6 +45130,12 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 + snakecase-keys@5.5.0: + dependencies: + map-obj: 4.3.0 + snake-case: 3.0.4 + type-fest: 3.13.1 + sockjs@0.3.24: dependencies: faye-websocket: 0.11.4 @@ -44452,7 +45145,7 @@ snapshots: socks-proxy-agent@8.0.5: dependencies: agent-base: 7.1.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -44754,6 +45447,8 @@ snapshots: steno@4.0.2: {} + store2@2.14.4: {} + stream-browserify@3.0.0: dependencies: inherits: 2.0.4 @@ -45017,6 +45712,8 @@ snapshots: symbol-observable@2.0.3: {} + symbol-observable@4.0.0: {} + symbol-tree@3.2.4: {} symbol.inspect@1.0.1: {} @@ -45365,6 +46062,10 @@ snapshots: ts-interface-checker@0.1.13: {} + ts-invariant@0.10.3: + dependencies: + tslib: 2.8.1 + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(esbuild@0.24.2)(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 @@ -45404,12 +46105,12 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.26.0) - ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.10.5))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)))(typescript@5.6.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.10.5) + jest: 29.7.0(@types/node@20.17.9)(ts-node@10.9.2(@swc/core@1.10.4(@swc/helpers@0.5.15))(@types/node@20.17.9)(typescript@5.6.3)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -45560,7 +46261,7 @@ snapshots: cac: 6.7.14 chokidar: 4.0.3 consola: 3.3.3 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 esbuild: 0.24.2 joycon: 3.1.1 picocolors: 1.1.1 @@ -45594,7 +46295,7 @@ snapshots: tuf-js@2.2.1: dependencies: '@tufjs/models': 2.0.1 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 make-fetch-happen: 13.0.1 transitivePeerDependencies: - supports-color @@ -45673,6 +46374,8 @@ snapshots: type-fest@2.19.0: {} + type-fest@3.13.1: {} + type-fest@4.31.0: {} type-is@1.6.18: @@ -46025,6 +46728,8 @@ snapshots: idb-keyval: 6.2.1 ioredis: 5.4.2 + untildify@4.0.0: {} + untyped@1.5.2: dependencies: '@babel/core': 7.26.0 @@ -46315,6 +47020,24 @@ snapshots: - supports-color - terser + vite-node@2.1.5(@types/node@20.17.9)(terser@5.37.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 1.1.2 + vite: 5.4.11(@types/node@20.17.9)(terser@5.37.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite-node@2.1.5(@types/node@22.10.5)(terser@5.37.0): dependencies: cac: 6.7.14 @@ -46503,6 +47226,42 @@ snapshots: - supports-color - terser + vitest@2.1.5(@types/node@20.17.9)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): + dependencies: + '@vitest/expect': 2.1.5 + '@vitest/mocker': 2.1.5(vite@5.4.11(@types/node@20.17.9)(terser@5.37.0)) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.5 + '@vitest/snapshot': 2.1.5 + '@vitest/spy': 2.1.5 + '@vitest/utils': 2.1.5 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.11(@types/node@20.17.9)(terser@5.37.0) + vite-node: 2.1.5(@types/node@20.17.9)(terser@5.37.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.17.9 + jsdom: 25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10) + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vitest@2.1.5(@types/node@22.10.5)(jsdom@25.0.1(bufferutil@4.0.9)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@5.0.10))(terser@5.37.0): dependencies: '@vitest/expect': 2.1.5 @@ -47402,6 +48161,12 @@ snapshots: toposort: 2.0.2 type-fest: 2.19.0 + zen-observable-ts@1.2.5: + dependencies: + zen-observable: 0.8.15 + + zen-observable@0.8.15: {} + zimmerframe@1.1.2: {} zlibjs@0.3.1: {} From 2c4b037018ddfebe8c73450f9d4d8f145bf3d89d Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Thu, 9 Jan 2025 16:35:30 +0100 Subject: [PATCH 04/12] feat: add bridge data fetcher and update tests --- .../src/actions/ibc-transfer/index.ts | 8 +- .../src/actions/ibc-transfer/schema.ts | 28 ++ .../services/bridge-denom-provider.ts | 26 ++ .../services/ibc-transfer-action-service.ts | 32 +- .../src/actions/ibc-transfer/types.ts | 16 +- packages/plugin-cosmos/src/index.ts | 6 +- .../src/providers/wallet/index.ts | 2 +- .../entities/cosmos-wallet-chains-data.ts | 17 +- .../plugin-cosmos/src/shared/interfaces.ts | 7 +- .../shared/services/bridge-data-fetcher.ts | 71 ++++ .../src/tests/bridge-data-fetcher.test.ts | 101 +++++ .../src/tests/bridge-denom-provider.test.ts | 83 ++++ ...cosmos-ibc-transfer-action-service.test.ts | 377 ++++++------------ .../tests/cosmos-wallet-chains-data.test.ts | 5 + packages/plugin-cosmos/tsup.config.ts | 1 - 15 files changed, 503 insertions(+), 277 deletions(-) create mode 100644 packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts create mode 100644 packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts create mode 100644 packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts create mode 100644 packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts index 6998112ac79..217d4e1c21a 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -18,12 +18,13 @@ import type { } from "../../shared/interfaces"; import { IBCTransferActionParams } from "./types"; import { IBCTransferAction } from "./services/ibc-transfer-action-service"; +import { bridgeDenomProvider } from "./services/bridge-denom-provider"; export const createIBCTransferAction = ( pluginOptions: ICosmosPluginOptions ) => ({ name: "COSMOS_IBC_TRANSFER", - description: "Transfer tokens between addresses on cosmos chains", + description: "Transfer tokens between addresses on cosmos chains", handler: async ( _runtime: IAgentRuntime, _message: Memory, @@ -63,12 +64,13 @@ export const createIBCTransferAction = ( const transferResp = await action.execute( paramOptions, + bridgeDenomProvider, customAssets ); if (_callback) { await _callback({ - text: `Successfully transferred ${paramOptions.amount} tokens from ${paramOptions.chainName} to ${paramOptions.toAddress} on ${paramOptions.targetChainName}\nGas paid: ${transferResp.gasPaid}\nTransaction Hash: ${transferResp.txHash}`, + text: `Successfully transferred ${paramOptions.amount} tokens from ${paramOptions.chainName} to ${paramOptions.toAddress} on ${paramOptions.targetChainName}\nTransaction Hash: ${transferResp.txHash}`, content: { success: true, hash: transferResp.txHash, @@ -84,7 +86,7 @@ export const createIBCTransferAction = ( agentId: _message.agentId, roomId: _message.roomId, content: { - text: `Transaction ${paramOptions.amount} ${paramOptions.symbol} to address ${paramOptions.toAddress} from chain ${paramOptions.chainName} to ${paramOptions.targetChainName} was successfully transferred.\n Gas paid: ${transferResp.gasPaid}. Tx hash: ${transferResp.txHash}`, + text: `Transaction ${paramOptions.amount} ${paramOptions.symbol} to address ${paramOptions.toAddress} from chain ${paramOptions.chainName} to ${paramOptions.targetChainName} was successfully transferred. Tx hash: ${transferResp.txHash}`, }, }; diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts index 62e30be681d..018e892d242 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts @@ -7,3 +7,31 @@ export const IBCTransferParamsSchema = z.object({ toAddress: z.string(), targetChainName: z.string(), }); + +export const bridgeDataProviderParamsSchema = z.object({ + source_asset_denom: z.string(), + source_asset_chain_id: z.string(), + allow_multi_tx: z.boolean(), +}); + +export const bridgeDataProviderResponseAssetsSchema = z.object({ + denom: z.string(), + chain_id: z.string(), + origin_denom: z.string(), + origin_chain_id: z.string(), + trace: z.string(), + symbol: z.string().optional(), + name: z.string().optional(), + logo_uri: z.string().optional(), + decimals: z.number().optional(), + recommended_symbol: z.string().optional(), +}); + +export const bridgeDataProviderResponseSchema = z.object({ + dest_assets: z.record( + z.string(), + z.object({ + assets: z.array(bridgeDataProviderResponseAssetsSchema), + }) + ), +}); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts new file mode 100644 index 00000000000..cab8e79e580 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts @@ -0,0 +1,26 @@ +import { IDenomProvider } from "../../../shared/interfaces"; +import { BridgeDataFetcher } from "../../../shared/services/bridge-data-fetcher"; + +export const bridgeDenomProvider: IDenomProvider = async ( + sourceAssetDenom: string, + sourceAssetChainId: string, + destChainId: string +) => { + const bridgeDataFetcher = BridgeDataFetcher.getInstance(); + const bridgeData = await bridgeDataFetcher.fetchBridgeData( + sourceAssetDenom, + sourceAssetChainId + ); + + const ibcAssetData = bridgeData.dest_assets[destChainId]?.assets?.find( + ({ origin_denom }) => origin_denom === sourceAssetDenom + ); + + if (!ibcAssetData.denom) { + throw new Error("No IBC asset data"); + } + + return { + denom: ibcAssetData.denom, + }; +}; diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts index 7a132eb2995..65c0ab72570 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/ibc-transfer-action-service.ts @@ -1,10 +1,12 @@ import { + convertDisplayUnitToBaseUnit, getAssetBySymbol, + getChainByChainId, getChainByChainName, - getChainIdByChainName, } from "@chain-registry/utils"; import { assets, chains } from "chain-registry"; import type { + IDenomProvider, ICosmosActionService, ICosmosPluginCustomChainData, ICosmosTransaction, @@ -20,6 +22,7 @@ export class IBCTransferAction implements ICosmosActionService { async execute( params: IBCTransferActionParams, + bridgeDenomProvider: IDenomProvider, customChainAssets?: ICosmosPluginCustomChainData["assets"][] ): Promise { const senderAddress = await this.cosmosWalletChains.getWalletAddress( @@ -66,6 +69,7 @@ export class IBCTransferAction implements ICosmosActionService { if (!denom.base) { throw new Error("Cannot find asset"); } + if (!sourceChain) { throw new Error("Cannot find source chain"); } @@ -74,23 +78,34 @@ export class IBCTransferAction implements ICosmosActionService { throw new Error("Cannot find destination chain"); } + const { denom: destAssetDenom } = await bridgeDenomProvider( + denom.base, + sourceChain.chain_id, + destChain.chain_id + ); + const route = await skipClient.route({ destAssetChainID: destChain.chain_id, - destAssetDenom: denom.base, + destAssetDenom, sourceAssetChainID: sourceChain.chain_id, sourceAssetDenom: denom.base, - amountOut: params.amount, + amountIn: convertDisplayUnitToBaseUnit( + availableAssets, + params.symbol, + params.amount, + params.chainName + ), + cumulativeAffiliateFeeBPS: "0", }); const userAddresses = await Promise.all( route.requiredChainAddresses.map(async (chainID) => { - const chainName = getChainIdByChainName(chains, chainID); + const chain = getChainByChainId(chains, chainID); return { chainID, - address: - await this.cosmosWalletChains.getWalletAddress( - chainName - ), + address: await this.cosmosWalletChains.getWalletAddress( + chain.chain_name + ), }; }) ); @@ -104,7 +119,6 @@ export class IBCTransferAction implements ICosmosActionService { txHash = executeRouteTxHash; }, }); - return { from: senderAddress, to: params.toAddress, diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts index 349bfb2fb81..ba62b9cd48d 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts @@ -1,4 +1,18 @@ import { z } from "zod"; -import { IBCTransferParamsSchema } from "./schema"; +import { + bridgeDataProviderParamsSchema, + bridgeDataProviderResponseAssetsSchema, + bridgeDataProviderResponseSchema, + IBCTransferParamsSchema, +} from "./schema"; export type IBCTransferActionParams = z.infer; +export type BridgeDataProviderParams = z.infer< + typeof bridgeDataProviderParamsSchema +>; +export type BridgeDataProviderResponseAsset = z.infer< + typeof bridgeDataProviderResponseAssetsSchema +>; +export type BridgeDataProviderResponse = z.infer< + typeof bridgeDataProviderResponseSchema +>; diff --git a/packages/plugin-cosmos/src/index.ts b/packages/plugin-cosmos/src/index.ts index ce2fff5c4b0..bbd3ace40ee 100644 --- a/packages/plugin-cosmos/src/index.ts +++ b/packages/plugin-cosmos/src/index.ts @@ -2,6 +2,7 @@ import { createTransferAction } from "./actions/transfer"; import type { Plugin } from "@elizaos/core"; import { createCosmosWalletProvider } from "./providers/wallet"; import { ICosmosPluginOptions } from "./shared/interfaces"; +import { createIBCTransferAction } from "./actions/ibc-transfer"; export const createCosmosPlugin = ( pluginOptions?: ICosmosPluginOptions @@ -11,7 +12,10 @@ export const createCosmosPlugin = ( providers: [createCosmosWalletProvider(pluginOptions)], evaluators: [], services: [], - actions: [createTransferAction(pluginOptions)], + actions: [ + createTransferAction(pluginOptions), + createIBCTransferAction(pluginOptions), + ], }); export default createCosmosPlugin; diff --git a/packages/plugin-cosmos/src/providers/wallet/index.ts b/packages/plugin-cosmos/src/providers/wallet/index.ts index 04b73bf2bb8..a1f3b9dfeaf 100644 --- a/packages/plugin-cosmos/src/providers/wallet/index.ts +++ b/packages/plugin-cosmos/src/providers/wallet/index.ts @@ -1,4 +1,4 @@ -import { IAgentRuntime } from "@ai16z/eliza"; +import { IAgentRuntime } from "@elizaos/core"; import { convertBaseUnitToDisplayUnit, getSymbolByDenom, diff --git a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts index 3c4cc70f9a4..076366c0daa 100644 --- a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts +++ b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts @@ -65,7 +65,12 @@ export class CosmosWalletChains implements ICosmosWalletChains { } public async getWalletAddress(chainName: string) { - return await this.walletChainsData[chainName].wallet.getWalletAddress(); + const chainWalletsForGivenChain = this.walletChainsData[chainName]; + if (!chainWalletsForGivenChain) { + throw new Error("Invalid chain name"); + } + + return await chainWalletsForGivenChain.wallet.getWalletAddress(); } public getSigningCosmWasmClient(chainName: string) { @@ -73,10 +78,12 @@ export class CosmosWalletChains implements ICosmosWalletChains { } public getSkipClient(chainName: string): SkipClient { - return this.walletChainsData[chainName].skipClient; - } + const chainWalletsForGivenChain = this.walletChainsData[chainName]; + + if (!chainWalletsForGivenChain) { + throw new Error("Invalid chain name"); + } - public async getUserAddress(chainName: string): Promise { - return this.walletChainsData[chainName].wallet.getWalletAddress(); + return chainWalletsForGivenChain.skipClient; } } diff --git a/packages/plugin-cosmos/src/shared/interfaces.ts b/packages/plugin-cosmos/src/shared/interfaces.ts index 2f4dc2438c3..b9978f821e6 100644 --- a/packages/plugin-cosmos/src/shared/interfaces.ts +++ b/packages/plugin-cosmos/src/shared/interfaces.ts @@ -48,9 +48,10 @@ export interface ICosmosWalletChainsData { [chainName: string]: ICosmosChainWallet; } -export interface IBridgeDataProvider { +export interface IDenomProvider { ( sourceAssetDenom: string, - sourceAssetChainId: string - ): Promise<{ channelId: string; portId: string; ibcDenom: string }>; + sourceAssetChainId: string, + destChainId: string + ): Promise<{ denom: string }>; } diff --git a/packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts b/packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts new file mode 100644 index 00000000000..84c44277c6f --- /dev/null +++ b/packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts @@ -0,0 +1,71 @@ +import { bridgeDataProviderResponseSchema } from "../../actions/ibc-transfer/schema"; +import { + BridgeDataProviderParams, + BridgeDataProviderResponse, +} from "../../actions/ibc-transfer/types"; +import axios from "axios"; + +type CacheKey = `${string}_${string}`; + +export class BridgeDataFetcher { + private static instance: BridgeDataFetcher; + private cache: Map; + private readonly apiUrl: string; + + private constructor() { + this.cache = new Map(); + this.apiUrl = "https://api.skip.build/v2/fungible/assets_from_source"; + } + + public static getInstance(): BridgeDataFetcher { + if (!BridgeDataFetcher.instance) { + BridgeDataFetcher.instance = new BridgeDataFetcher(); + } + return BridgeDataFetcher.instance; + } + + private generateCacheKey( + sourceAssetDenom: string, + sourceAssetChainId: string + ): CacheKey { + return `${sourceAssetDenom}_${sourceAssetChainId}`; + } + + public async fetchBridgeData( + sourceAssetDenom: string, + sourceAssetChainId: string + ): Promise { + const cacheKey = this.generateCacheKey( + sourceAssetDenom, + sourceAssetChainId + ); + + if (this.cache.has(cacheKey)) { + return this.cache.get(cacheKey)!; + } + + const requestData: BridgeDataProviderParams = { + source_asset_denom: sourceAssetDenom, + source_asset_chain_id: sourceAssetChainId, + allow_multi_tx: false, + }; + + try { + const response = await axios.post(this.apiUrl, requestData, { + headers: { + "Content-Type": "application/json", + }, + }); + + const validResponse = bridgeDataProviderResponseSchema.parse( + response.data + ); + + this.cache.set(cacheKey, validResponse); + return response.data; + } catch (error) { + console.error("Error fetching assets:", error); + throw error; + } + } +} diff --git a/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts b/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts new file mode 100644 index 00000000000..d7c7955a0b9 --- /dev/null +++ b/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { BridgeDataFetcher } from "../shared/services/bridge-data-fetcher"; +import axios from "axios"; + +vi.mock("axios"); + +describe("BridgeDataFetcher", () => { + let fetcher: BridgeDataFetcher; + + beforeEach(() => { + fetcher = BridgeDataFetcher.getInstance(); + vi.clearAllMocks(); + }); + + it("should return the same instance from getInstance", () => { + const fetcher1 = BridgeDataFetcher.getInstance(); + const fetcher2 = BridgeDataFetcher.getInstance(); + expect(fetcher1).toBe(fetcher2); + }); + + it("should use cache when data is already fetched", async () => { + const mockResponse = { + dest_assets: { + someKey: { + assets: [ + { + denom: "atom", + chain_id: "cosmos", + origin_denom: "atom", + origin_chain_id: "cosmos", + trace: "someTrace", + symbol: "ATOM", + name: "Cosmos Atom", + logo_uri: "http://someurl.com/logo.png", + decimals: 6, + recommended_symbol: "ATOM", + }, + ], + }, + }, + }; + + // @ts-expect-error -- ... + axios.post.mockResolvedValueOnce({ data: mockResponse }); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); + + expect(axios.post).toHaveBeenCalledTimes(1); + + await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); + expect(axios.post).toHaveBeenCalledTimes(1); // axios nie powinien być wywołany ponownie + }); + + it("should fetch and cache data correctly", async () => { + const mockResponse = { + dest_assets: { + someKey: { + assets: [ + { + denom: "atom", + chain_id: "cosmos", + origin_denom: "atom", + origin_chain_id: "cosmos", + trace: "someTrace", + symbol: "ATOM", + name: "Cosmos Atom", + logo_uri: "http://someurl.com/logo.png", + decimals: 6, + recommended_symbol: "ATOM", + }, + ], + }, + }, + }; + + // @ts-expect-error -- ... + axios.post.mockResolvedValueOnce({ data: mockResponse }); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + + const result = await fetcher.fetchBridgeData( + sourceAssetDenom, + sourceAssetChainId + ); + + expect(result).toEqual(mockResponse); + + const cacheKey = `${sourceAssetDenom}_${sourceAssetChainId}`; + expect(fetcher["cache"].has(cacheKey)).toBe(true); + + const cachedResult = await fetcher.fetchBridgeData( + sourceAssetDenom, + sourceAssetChainId + ); + expect(cachedResult).toEqual(mockResponse); + }); +}); diff --git a/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts b/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts new file mode 100644 index 00000000000..2363a0e99ae --- /dev/null +++ b/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts @@ -0,0 +1,83 @@ +import { vi, expect, it, beforeEach, describe } from "vitest"; +import { BridgeDataFetcher } from "../shared/services/bridge-data-fetcher"; +import { bridgeDenomProvider } from "../actions/ibc-transfer/services/bridge-denom-provider"; + +vi.mock("./bridge-data-fetcher", () => ({ + BridgeDataFetcher: { + getInstance: vi.fn().mockReturnValue({ + fetchBridgeData: vi.fn(), + }), + }, +})); + +describe("bridgeDataProvider", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let mockFetchBridgeData: any; + + beforeEach(() => { + mockFetchBridgeData = vi.fn(); + BridgeDataFetcher.getInstance().fetchBridgeData = mockFetchBridgeData; + }); + + it("should return correct channelId and ibcDenom when valid data is returned", async () => { + const mockResponse = { + dest_assets: { + osmos: { + assets: [ + { + origin_denom: "atom", + denom: "uatom", + trace: "channel-123/abc", + }, + ], + }, + }, + }; + + mockFetchBridgeData.mockResolvedValue(mockResponse); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + const destinationAdssetChainId = "osmos"; + + const result = await bridgeDenomProvider( + sourceAssetDenom, + sourceAssetChainId, + destinationAdssetChainId + ); + + expect(result).toEqual({ + denom: "uatom", + }); + }); + + it("should throw an error when ibcAssetData is not found", async () => { + const mockResponse = { + dest_assets: { + osmos: { + assets: [ + { + origin_denom: "btc", + denom: "ubtc", + trace: "channel-123/abc", + }, + ], + }, + }, + }; + + mockFetchBridgeData.mockResolvedValue(mockResponse); + + const sourceAssetDenom = "atom"; + const sourceAssetChainId = "cosmos"; + const destinationAdssetChainId = "osmos"; + + await expect( + bridgeDenomProvider( + sourceAssetDenom, + sourceAssetChainId, + destinationAdssetChainId + ) + ).rejects.toThrowError(); + }); +}); diff --git a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts index 1c1b3dbf7b3..03311aca8d4 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts @@ -1,309 +1,180 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { IBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; -import { IBCTransferActionParams } from "../actions/ibc-transfer/types"; -import { getAssetBySymbol, getChainByChainName } from "@chain-registry/utils"; - -vi.mock("@cosmjs/cosmwasm-stargate", () => ({ - SigningCosmWasmClient: { - connectWithSigner: vi.fn(), - }, -})); +import { IBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; // dostosuj ścieżkę do pliku +import { assets } from "chain-registry"; +import * as CosmosAssetsHelpers from "../shared/helpers/cosmos-assets"; +import { getAssetBySymbol } from "@chain-registry/utils"; +import { getAvailableAssets } from "../shared/helpers/cosmos-assets"; vi.mock("@chain-registry/utils", () => ({ - getAssetBySymbol: vi.fn().mockReturnValue({ base: "uatom" }), - getChainByChainName: vi - .fn() - .mockResolvedValue({ chain_id: "cosmos-chain-id" }), - convertDisplayUnitToBaseUnit: vi.fn().mockResolvedValue("100000000"), -})); - -vi.mock("../shared/services/cosmos-transaction-fee-estimator", () => ({ - CosmosTransactionFeeEstimator: { - estimateGasForIBCTransfer: vi.fn().mockResolvedValue(BigInt(200_000)), - }, + getAssetBySymbol: vi.fn(), + getChainByChainName: vi.fn((_, chainName: string) => { + if (chainName === "test-chain") return { chain_id: "source-chain-id" }; + return { chain_id: "target-chain-id" }; + }), + convertDisplayUnitToBaseUnit: vi.fn(() => "1"), + getChainByChainId: vi.fn(() => ({ chainId: "target-chain-id" })), })); vi.mock("../shared/helpers/cosmos-assets", () => ({ - getAvailableAssets: vi.fn().mockResolvedValue([]), -})); - -vi.mock("../shared/helpers/cosmos-transaction-receipt.ts", () => ({ - getPaidFeeFromReceipt: vi.fn().mockReturnValue("200000"), + getAvailableAssets: vi.fn(), })); describe("IBCTransferAction", () => { - const mockSigningCosmWasmClient = { - sendTokens: vi.fn().mockResolvedValue({ - transactionHash: "mockTxHash", - }), + const mockWalletChains = { + getWalletAddress: vi.fn(), + getSkipClient: vi.fn(), }; - const mockCosmosWalletChains = { - walletChainsData: {}, - getWalletAddress: vi.fn().mockResolvedValue("senderAddress"), - getSigningCosmWasmClient: vi - .fn() - .mockReturnValue(mockSigningCosmWasmClient), + const mockBridgeDenomProvider = vi.fn(); + const mockSkipClient = { + route: vi.fn(), + executeRoute: vi.fn(), }; - beforeEach(() => { - vi.clearAllMocks(); - }); - - it("should execute transfer successfully", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains - ); + const params = { + chainName: "test-chain", + targetChainName: "target-chain", + symbol: "ATOM", + amount: "10", + toAddress: "cosmos1receiveraddress", + }; - const expectedResult = { - from: "senderAddress", - to: "cosmosReceiverAddress", - gasPaid: "200000", - txHash: "mockTxHash", - }; + const customChainAssets = []; - await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).resolves.toEqual(expectedResult); + beforeEach(() => { + vi.clearAllMocks(); + mockWalletChains.getSkipClient.mockReturnValue(mockSkipClient); }); - it("should throw error if transaction fails", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - - mockSigningCosmWasmClient.sendTokens.mockRejectedValue( - new Error("Transaction Failed") - ); - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains - ); + it("throws an error if sender address is not available", async () => { + mockWalletChains.getWalletAddress.mockResolvedValue(null); + // @ts-expect-error --- ... + const ibcTransferAction = new IBCTransferAction(mockWalletChains); await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).rejects.toThrow("Transaction Failed"); - }); - - it("should throw error if no wallet address is found", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - - mockCosmosWalletChains.getWalletAddress.mockResolvedValue(null); // Brak adresu portfela - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains + ibcTransferAction.execute( + params, + mockBridgeDenomProvider, + customChainAssets + ) + ).rejects.toThrow( + `Cannot get wallet address for chain ${params.chainName}` ); - - await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).rejects.toThrow("Cannot get wallet address for chain cosmos"); }); - it("should throw error if no receiver address is provided", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - mockCosmosWalletChains.getWalletAddress.mockResolvedValue( - "cosmos1address" - ); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains + it("throws an error if receiver address is missing", async () => { + const invalidParams = { ...params, toAddress: undefined }; + mockWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1senderaddress" ); + // @ts-expect-error --- ... + const ibcTransferAction = new IBCTransferAction(mockWalletChains); await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ibcTransferAction.execute( + invalidParams, + mockBridgeDenomProvider, + customChainAssets + ) ).rejects.toThrow("No receiver address"); }); - it("should throw error if no symbol is provided", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "", - amount: "100", - }; - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - - mockCosmosWalletChains.getWalletAddress.mockResolvedValue( - "cosmos1address" - ); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains + it("throws an error if target chain name is missing", async () => { + const invalidParams = { ...params, targetChainName: undefined }; + mockWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1senderaddress" ); + // @ts-expect-error --- ... + const ibcTransferAction = new IBCTransferAction(mockWalletChains); await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).rejects.toThrow("No symbol"); + ibcTransferAction.execute( + invalidParams, + mockBridgeDenomProvider, + customChainAssets + ) + ).rejects.toThrow("No target chain name"); }); - it("should throw error if no chainName is provided", async () => { - const params: IBCTransferActionParams = { - chainName: "", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - - mockCosmosWalletChains.getWalletAddress.mockResolvedValue( - "cosmos1address" - ); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains + it("throws an error if symbol is missing", async () => { + const invalidParams = { ...params, symbol: undefined }; + mockWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1senderaddress" ); + // @ts-expect-error --- ... + const ibcTransferAction = new IBCTransferAction(mockWalletChains); await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).rejects.toThrow("No chain name"); + ibcTransferAction.execute( + invalidParams, + mockBridgeDenomProvider, + customChainAssets + ) + ).rejects.toThrow("No symbol"); }); - it("should throw error if no targetChainName is provided", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "", - symbol: "atom", - amount: "100", - }; - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", - }); - - mockCosmosWalletChains.getWalletAddress.mockResolvedValue( - "cosmos1address" - ); - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains + it("throws an error if asset cannot be found", async () => { + mockWalletChains.getWalletAddress.mockResolvedValue( + "cosmos1senderaddress" ); - await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).rejects.toThrow("No target chain name"); - }); - it("should throw error if no base denom in assets list", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - - // @ts-expect-error --- - getAssetBySymbol.mockReturnValue({}); - - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", + vi.spyOn(CosmosAssetsHelpers, "getAvailableAssets").mockReturnValue([]); + // @ts-expect-error --- ... + getAssetBySymbol.mockReturnValue({ + base: null, }); - mockCosmosWalletChains.getWalletAddress.mockResolvedValue( - "cosmos1address" - ); - - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains - ); + // @ts-expect-error --- ... + const ibcTransferAction = new IBCTransferAction(mockWalletChains); await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) + ibcTransferAction.execute( + params, + mockBridgeDenomProvider, + customChainAssets + ) ).rejects.toThrow("Cannot find asset"); }); - it("should throw error if no chain in chain list", async () => { - const params: IBCTransferActionParams = { - chainName: "cosmos", - toAddress: "cosmosReceiverAddress", - targetChainName: "cosmosTarget", - symbol: "atom", - amount: "100", - }; - // @ts-expect-error --- ... - getAssetBySymbol.mockReturnValue({ base: "uatom" }); + it("executes the IBC transfer successfully", async () => { + const senderAddress = "cosmos1senderaddress"; + const targetChainId = "target-chain-id"; + const sourceChainId = "source-chain-id"; + + mockWalletChains.getWalletAddress.mockResolvedValue(senderAddress); // @ts-expect-error --- ... - getChainByChainName.mockReturnValue(undefined); + getAvailableAssets.mockReturnValue(assets); - const mockBridgeDataProvider = vi.fn().mockResolvedValue({ - ibcDenom: "uatom", - channelId: "channel-1", - portId: "transfer", + // @ts-expect-error --- ... + getAssetBySymbol.mockReturnValue({ + base: "uatom", }); + const params = { + chainName: "test-chain", + targetChainName: "target-chain", + symbol: "ATOM", + amount: "10", + toAddress: "cosmos1receiveraddress", + }; - mockCosmosWalletChains.getWalletAddress.mockResolvedValue( - "cosmos1address" - ); + mockBridgeDenomProvider.mockResolvedValue({ denom: "uatom" }); + mockSkipClient.route.mockResolvedValue({ + requiredChainAddresses: [sourceChainId, targetChainId], + }); + // @ts-expect-error --- ... + const ibcTransferAction = new IBCTransferAction(mockWalletChains); - const cosmosIBCTransferAction = new IBCTransferAction( - mockCosmosWalletChains + const result = await ibcTransferAction.execute( + params, + mockBridgeDenomProvider, + customChainAssets ); - await expect( - cosmosIBCTransferAction.execute(params, mockBridgeDataProvider) - ).rejects.toThrow("Cannot find chain"); + expect(result).toEqual({ + from: senderAddress, + to: params.toAddress, + txHash: undefined, + }); + expect(mockSkipClient.executeRoute).toHaveBeenCalled(); }); }); diff --git a/packages/plugin-cosmos/src/tests/cosmos-wallet-chains-data.test.ts b/packages/plugin-cosmos/src/tests/cosmos-wallet-chains-data.test.ts index 7fbcf9806b8..7563e3ff9b0 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-wallet-chains-data.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-wallet-chains-data.test.ts @@ -17,6 +17,10 @@ vi.mock("@cosmjs/cosmwasm-stargate", () => ({ }, })); +vi.mock("@skip-go/client", () => ({ + SkipClient: vi.fn(() => ({})), +})); + vi.mock("../shared/entities/cosmos-wallet.ts", () => ({ CosmosWallet: { create: vi.fn(), @@ -74,6 +78,7 @@ describe("CosmosWalletChains", () => { chain1: { wallet: mockCosmosWalletCreate, signingCosmWasmClient: {}, + skipClient: {}, }, }, }; diff --git a/packages/plugin-cosmos/tsup.config.ts b/packages/plugin-cosmos/tsup.config.ts index 90948913ae9..12d9ae64f96 100644 --- a/packages/plugin-cosmos/tsup.config.ts +++ b/packages/plugin-cosmos/tsup.config.ts @@ -21,6 +21,5 @@ export default defineConfig({ "@cosmjs/proto-signing", "@cosmjs/cosmwasm-stargate", "zod", - "@ai16z/eliza", ], }); From 8e467ff6bc1252518b3aaff99bf4f34c4ed17bc7 Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Thu, 9 Jan 2025 16:50:10 +0100 Subject: [PATCH 05/12] update: add IBC transfer action to README --- packages/plugin-cosmos/README.md | 55 ++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/packages/plugin-cosmos/README.md b/packages/plugin-cosmos/README.md index f32f58a5224..18f26bc2c98 100644 --- a/packages/plugin-cosmos/README.md +++ b/packages/plugin-cosmos/README.md @@ -7,15 +7,17 @@ This plugin provides actions and utilities for interacting with Cosmos-compatibl ## Development Prepare Eliza according to [README](../../README.md) - Add variables required for `@ai16z/plugin-cosmos` : - ``` +Add variables required for `@ai16z/plugin-cosmos` : + +``` COSMOS_RECOVERY_PHRASE=your recovery phrase words COSMOS_AVAILABLE_CHAINS=chain1,chain2,chain3 - ``` +``` Ensure the appropriate environment variables are added for the plugin. If they are correctly configured, the project will run with `@ai16z/plugin-cosmos` -Run Eliza +Run Eliza + ``` pnpm run dev ``` @@ -26,13 +28,12 @@ pnpm run dev To start using the plugin, you need to provide your **Cosmos account recovery phrases** and the list of **available chains**. Add the following to your `.env` file: - ```env COSMOS_RECOVERY_PHRASE=your recovery phrase words COSMOS_AVAILABLE_CHAINS=chain1,chain2,chain3 ``` -Ensure that the chain names in `COSMOS_AVAILABLE_CHAINS` match the identifiers from the [chain-registry](https://github.com/cosmos/chain-registry) library for compatibility. +Ensure that the chain names in `COSMOS_AVAILABLE_CHAINS` match the identifiers from the [chain-registry](https://github.com/cosmos/chain-registry) library for compatibility. ### Using the Cosmos Helper Character @@ -51,9 +52,11 @@ To use the character, pass it with the `--characters` flag: --- ### Custom chain configuration + Plugin allows you to pass you custom chain config to `createCosmosPlugin` function invoked in `../agent/src/index`. Your custom configuration fulfills the interfaces from `chain-registry` + ``` import type { assets, chains } from "chain-registry"; @@ -99,6 +102,46 @@ Yes 4. Action executed. +### Token IBC Transfer + +This plugin supports a token transfer action, which allows users to transfer tokens between addresses on Cosmos-compatible blockchains between different chains. + +#### Requirements + +You need to set both chain that you want to transfer founds between in env file. For example: + +``` +COSMOS_AVAILABLE_CHAINS=osmosistestnet,neutrontestnet +``` + +If you want to transfer your tokens between Osmosis Testnet and Neutron Testnet + +#### Example Prompts + +Below are examples of how the ibc transfer action can be initiated and confirmed: + +**Example** + +1. User input: + +``` +Make an IBC transfer 0.0001 OSMO to neutron1nk3uuw6zt5t5aqw5fvujkd54sa4uws9xg2nk82 from osmosistestnet to neutrontestnet +``` + +2. Plugin response: + +``` +Before making the IBC transfer, I would like to confirm the details. You would like to transfer 0.0001 OSMO from osmosistestnet to neutrontestnet, specifically to the address neutron1nk3uuw6zt5t5aqw5fvujkd54sa4uws9xg2nk82, is that correct? +``` + +3. User confirmation: + +``` +Yes +``` + +4. Action executed. + --- ## Contribution From 63acdd4968e8e698e901d97ff5ba80e00fb05e9a Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Fri, 10 Jan 2025 11:03:19 +0100 Subject: [PATCH 06/12] refactor: make bridge data fetcher reusable across the project --- packages/plugin-cosmos/README.md | 6 +-- .../src/actions/ibc-transfer/schema.ts | 28 -------------- .../services/bridge-denom-provider.ts | 7 ++-- .../src/actions/ibc-transfer/types.ts | 16 +------- .../assets-from-source-fetcher/interfaces.ts | 16 ++++++++ .../assets-from-source-fetcher/schema.ts | 29 +++++++++++++++ .../skip-api-assets-from-source-fetcher.ts} | 37 ++++++++++--------- .../src/shared/services/skip-api/config.ts | 1 + .../src/tests/bridge-denom-provider.test.ts | 7 ++-- ...ip-api-assets-from-source-fetcher.test.ts} | 22 +++++------ 10 files changed, 89 insertions(+), 80 deletions(-) create mode 100644 packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/interfaces.ts create mode 100644 packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/schema.ts rename packages/plugin-cosmos/src/shared/services/{bridge-data-fetcher.ts => skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts} (56%) create mode 100644 packages/plugin-cosmos/src/shared/services/skip-api/config.ts rename packages/plugin-cosmos/src/tests/{bridge-data-fetcher.test.ts => skip-api-assets-from-source-fetcher.test.ts} (78%) diff --git a/packages/plugin-cosmos/README.md b/packages/plugin-cosmos/README.md index 18f26bc2c98..610489e8638 100644 --- a/packages/plugin-cosmos/README.md +++ b/packages/plugin-cosmos/README.md @@ -1,4 +1,4 @@ -# `@ai16z/plugin-cosmos` +# `@elizaos/plugin-cosmos` This plugin provides actions and utilities for interacting with Cosmos-compatible blockchains. @@ -7,14 +7,14 @@ This plugin provides actions and utilities for interacting with Cosmos-compatibl ## Development Prepare Eliza according to [README](../../README.md) -Add variables required for `@ai16z/plugin-cosmos` : +Add variables required for `@elizaos/plugin-cosmos` : ``` COSMOS_RECOVERY_PHRASE=your recovery phrase words COSMOS_AVAILABLE_CHAINS=chain1,chain2,chain3 ``` -Ensure the appropriate environment variables are added for the plugin. If they are correctly configured, the project will run with `@ai16z/plugin-cosmos` +Ensure the appropriate environment variables are added for the plugin. If they are correctly configured, the project will run with `@elizaos/plugin-cosmos` Run Eliza diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts index 018e892d242..62e30be681d 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts @@ -7,31 +7,3 @@ export const IBCTransferParamsSchema = z.object({ toAddress: z.string(), targetChainName: z.string(), }); - -export const bridgeDataProviderParamsSchema = z.object({ - source_asset_denom: z.string(), - source_asset_chain_id: z.string(), - allow_multi_tx: z.boolean(), -}); - -export const bridgeDataProviderResponseAssetsSchema = z.object({ - denom: z.string(), - chain_id: z.string(), - origin_denom: z.string(), - origin_chain_id: z.string(), - trace: z.string(), - symbol: z.string().optional(), - name: z.string().optional(), - logo_uri: z.string().optional(), - decimals: z.number().optional(), - recommended_symbol: z.string().optional(), -}); - -export const bridgeDataProviderResponseSchema = z.object({ - dest_assets: z.record( - z.string(), - z.object({ - assets: z.array(bridgeDataProviderResponseAssetsSchema), - }) - ), -}); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts index cab8e79e580..3fcfdab3705 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/services/bridge-denom-provider.ts @@ -1,13 +1,14 @@ import { IDenomProvider } from "../../../shared/interfaces"; -import { BridgeDataFetcher } from "../../../shared/services/bridge-data-fetcher"; +import { SkipApiAssetsFromSourceFetcher } from "../../../shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher"; export const bridgeDenomProvider: IDenomProvider = async ( sourceAssetDenom: string, sourceAssetChainId: string, destChainId: string ) => { - const bridgeDataFetcher = BridgeDataFetcher.getInstance(); - const bridgeData = await bridgeDataFetcher.fetchBridgeData( + const skipApiAssetsFromSourceFetcher = + SkipApiAssetsFromSourceFetcher.getInstance(); + const bridgeData = await skipApiAssetsFromSourceFetcher.fetch( sourceAssetDenom, sourceAssetChainId ); diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts index ba62b9cd48d..349bfb2fb81 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/types.ts @@ -1,18 +1,4 @@ import { z } from "zod"; -import { - bridgeDataProviderParamsSchema, - bridgeDataProviderResponseAssetsSchema, - bridgeDataProviderResponseSchema, - IBCTransferParamsSchema, -} from "./schema"; +import { IBCTransferParamsSchema } from "./schema"; export type IBCTransferActionParams = z.infer; -export type BridgeDataProviderParams = z.infer< - typeof bridgeDataProviderParamsSchema ->; -export type BridgeDataProviderResponseAsset = z.infer< - typeof bridgeDataProviderResponseAssetsSchema ->; -export type BridgeDataProviderResponse = z.infer< - typeof bridgeDataProviderResponseSchema ->; diff --git a/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/interfaces.ts b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/interfaces.ts new file mode 100644 index 00000000000..06b1ba5aa7a --- /dev/null +++ b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/interfaces.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; +import { + skipApiAssetsFromSourceParamsSchema, + skipApiAssetsFromSourceResponseAssetSchema, + skipApiAssetsFromSourceResponseSchema, +} from "./schema"; + +export type SkipApiAssetsFromSourceParams = z.infer< + typeof skipApiAssetsFromSourceParamsSchema +>; +export type SkipApiAssetsFromSourceResponseAsset = z.infer< + typeof skipApiAssetsFromSourceResponseAssetSchema +>; +export type SkipApiAssetsFromSourceResponse = z.infer< + typeof skipApiAssetsFromSourceResponseSchema +>; diff --git a/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/schema.ts b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/schema.ts new file mode 100644 index 00000000000..6272e8a1001 --- /dev/null +++ b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/schema.ts @@ -0,0 +1,29 @@ +import { z } from "zod"; + +export const skipApiAssetsFromSourceParamsSchema = z.object({ + source_asset_denom: z.string(), + source_asset_chain_id: z.string(), + allow_multi_tx: z.boolean(), +}); + +export const skipApiAssetsFromSourceResponseAssetSchema = z.object({ + denom: z.string(), + chain_id: z.string(), + origin_denom: z.string(), + origin_chain_id: z.string(), + trace: z.string(), + symbol: z.string().optional(), + name: z.string().optional(), + logo_uri: z.string().optional(), + decimals: z.number().optional(), + recommended_symbol: z.string().optional(), +}); + +export const skipApiAssetsFromSourceResponseSchema = z.object({ + dest_assets: z.record( + z.string(), + z.object({ + assets: z.array(skipApiAssetsFromSourceResponseAssetSchema), + }) + ), +}); diff --git a/packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts similarity index 56% rename from packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts rename to packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts index 84c44277c6f..caad69d4b3b 100644 --- a/packages/plugin-cosmos/src/shared/services/bridge-data-fetcher.ts +++ b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts @@ -1,27 +1,30 @@ -import { bridgeDataProviderResponseSchema } from "../../actions/ibc-transfer/schema"; -import { - BridgeDataProviderParams, - BridgeDataProviderResponse, -} from "../../actions/ibc-transfer/types"; import axios from "axios"; +import { skipApiAssetsFromSourceResponseSchema } from "./schema"; +import { + SkipApiAssetsFromSourceParams, + SkipApiAssetsFromSourceResponse, +} from "./interfaces"; +import { skipApiBaseUrl } from "../config"; type CacheKey = `${string}_${string}`; +const endpointPath = "fungible/assets_from_source"; -export class BridgeDataFetcher { - private static instance: BridgeDataFetcher; - private cache: Map; +export class SkipApiAssetsFromSourceFetcher { + private static instance: SkipApiAssetsFromSourceFetcher; + private cache: Map; private readonly apiUrl: string; private constructor() { this.cache = new Map(); - this.apiUrl = "https://api.skip.build/v2/fungible/assets_from_source"; + this.apiUrl = `${skipApiBaseUrl}${endpointPath}`; } - public static getInstance(): BridgeDataFetcher { - if (!BridgeDataFetcher.instance) { - BridgeDataFetcher.instance = new BridgeDataFetcher(); + public static getInstance(): SkipApiAssetsFromSourceFetcher { + if (!SkipApiAssetsFromSourceFetcher.instance) { + SkipApiAssetsFromSourceFetcher.instance = + new SkipApiAssetsFromSourceFetcher(); } - return BridgeDataFetcher.instance; + return SkipApiAssetsFromSourceFetcher.instance; } private generateCacheKey( @@ -31,10 +34,10 @@ export class BridgeDataFetcher { return `${sourceAssetDenom}_${sourceAssetChainId}`; } - public async fetchBridgeData( + public async fetch( sourceAssetDenom: string, sourceAssetChainId: string - ): Promise { + ): Promise { const cacheKey = this.generateCacheKey( sourceAssetDenom, sourceAssetChainId @@ -44,7 +47,7 @@ export class BridgeDataFetcher { return this.cache.get(cacheKey)!; } - const requestData: BridgeDataProviderParams = { + const requestData: SkipApiAssetsFromSourceParams = { source_asset_denom: sourceAssetDenom, source_asset_chain_id: sourceAssetChainId, allow_multi_tx: false, @@ -57,7 +60,7 @@ export class BridgeDataFetcher { }, }); - const validResponse = bridgeDataProviderResponseSchema.parse( + const validResponse = skipApiAssetsFromSourceResponseSchema.parse( response.data ); diff --git a/packages/plugin-cosmos/src/shared/services/skip-api/config.ts b/packages/plugin-cosmos/src/shared/services/skip-api/config.ts new file mode 100644 index 00000000000..d3d4d5a16e7 --- /dev/null +++ b/packages/plugin-cosmos/src/shared/services/skip-api/config.ts @@ -0,0 +1 @@ +export const skipApiBaseUrl = "https://api.skip.build/v2/"; diff --git a/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts b/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts index 2363a0e99ae..622c1dde262 100644 --- a/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts +++ b/packages/plugin-cosmos/src/tests/bridge-denom-provider.test.ts @@ -1,11 +1,11 @@ import { vi, expect, it, beforeEach, describe } from "vitest"; -import { BridgeDataFetcher } from "../shared/services/bridge-data-fetcher"; import { bridgeDenomProvider } from "../actions/ibc-transfer/services/bridge-denom-provider"; +import { SkipApiAssetsFromSourceFetcher } from "../shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher"; vi.mock("./bridge-data-fetcher", () => ({ BridgeDataFetcher: { getInstance: vi.fn().mockReturnValue({ - fetchBridgeData: vi.fn(), + fetch: vi.fn(), }), }, })); @@ -16,7 +16,8 @@ describe("bridgeDataProvider", () => { beforeEach(() => { mockFetchBridgeData = vi.fn(); - BridgeDataFetcher.getInstance().fetchBridgeData = mockFetchBridgeData; + SkipApiAssetsFromSourceFetcher.getInstance().fetch = + mockFetchBridgeData; }); it("should return correct channelId and ibcDenom when valid data is returned", async () => { diff --git a/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts b/packages/plugin-cosmos/src/tests/skip-api-assets-from-source-fetcher.test.ts similarity index 78% rename from packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts rename to packages/plugin-cosmos/src/tests/skip-api-assets-from-source-fetcher.test.ts index d7c7955a0b9..fe69a7ad63d 100644 --- a/packages/plugin-cosmos/src/tests/bridge-data-fetcher.test.ts +++ b/packages/plugin-cosmos/src/tests/skip-api-assets-from-source-fetcher.test.ts @@ -1,20 +1,20 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { BridgeDataFetcher } from "../shared/services/bridge-data-fetcher"; import axios from "axios"; +import { SkipApiAssetsFromSourceFetcher } from "../shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher"; vi.mock("axios"); -describe("BridgeDataFetcher", () => { - let fetcher: BridgeDataFetcher; +describe("SkipApiAssetsFromSourceFetcher", () => { + let fetcher: SkipApiAssetsFromSourceFetcher; beforeEach(() => { - fetcher = BridgeDataFetcher.getInstance(); + fetcher = SkipApiAssetsFromSourceFetcher.getInstance(); vi.clearAllMocks(); }); it("should return the same instance from getInstance", () => { - const fetcher1 = BridgeDataFetcher.getInstance(); - const fetcher2 = BridgeDataFetcher.getInstance(); + const fetcher1 = SkipApiAssetsFromSourceFetcher.getInstance(); + const fetcher2 = SkipApiAssetsFromSourceFetcher.getInstance(); expect(fetcher1).toBe(fetcher2); }); @@ -46,12 +46,12 @@ describe("BridgeDataFetcher", () => { const sourceAssetDenom = "atom"; const sourceAssetChainId = "cosmos"; - await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); + await fetcher.fetch(sourceAssetDenom, sourceAssetChainId); expect(axios.post).toHaveBeenCalledTimes(1); - await fetcher.fetchBridgeData(sourceAssetDenom, sourceAssetChainId); - expect(axios.post).toHaveBeenCalledTimes(1); // axios nie powinien być wywołany ponownie + await fetcher.fetch(sourceAssetDenom, sourceAssetChainId); + expect(axios.post).toHaveBeenCalledTimes(1); }); it("should fetch and cache data correctly", async () => { @@ -82,7 +82,7 @@ describe("BridgeDataFetcher", () => { const sourceAssetDenom = "atom"; const sourceAssetChainId = "cosmos"; - const result = await fetcher.fetchBridgeData( + const result = await fetcher.fetch( sourceAssetDenom, sourceAssetChainId ); @@ -92,7 +92,7 @@ describe("BridgeDataFetcher", () => { const cacheKey = `${sourceAssetDenom}_${sourceAssetChainId}`; expect(fetcher["cache"].has(cacheKey)).toBe(true); - const cachedResult = await fetcher.fetchBridgeData( + const cachedResult = await fetcher.fetch( sourceAssetDenom, sourceAssetChainId ); From 907ed038ab4dbca305166618f4d7d0ff71c3e2e4 Mon Sep 17 00:00:00 2001 From: KacperKoza34 Date: Fri, 10 Jan 2025 11:06:50 +0100 Subject: [PATCH 07/12] fix: update similes for ibc transfer action --- packages/plugin-cosmos/src/actions/ibc-transfer/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts index 217d4e1c21a..88e92015622 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -218,9 +218,9 @@ export const createIBCTransferAction = ( ], ], similes: [ - "COSMOS_TRANSFER", - "COSMOS_SEND_TOKENS", - "COSMOS_TOKEN_TRANSFER", - "COSMOS_MOVE_TOKENS", + "COSMOS_BRIDGE_TOKEN", + "COSMOS_IBC_SEND_TOKEN", + "COSMOS_TOKEN_IBC_TRANSFER", + "COSMOS_MOVE_IBC_TOKENS", ], }); From 2e3e05edb6df6893a98db636ae0440857d1f118f Mon Sep 17 00:00:00 2001 From: stanislawkurzypBD Date: Fri, 10 Jan 2025 11:13:44 +0100 Subject: [PATCH 08/12] ELIZAAI-18 IBC swap action initialisation added --- agent/package.json | 4 +- agent/src/index.ts | 2 +- packages/plugin-cosmos/package.json | 2 +- .../src/actions/ibc-swap/index.ts | 198 ++++++++++++++++++ .../src/actions/ibc-swap/schema.ts | 9 + .../services/ibc-swap-action-service.ts | 143 +++++++++++++ .../src/actions/ibc-swap/types.ts | 4 + .../src/actions/ibc-transfer/index.ts | 2 +- .../src/actions/transfer/index.ts | 2 +- packages/plugin-cosmos/src/index.ts | 5 +- .../src/providers/wallet/index.ts | 2 +- .../src/providers/wallet/utils.ts | 2 +- .../entities/cosmos-wallet-chains-data.ts | 15 +- .../plugin-cosmos/src/shared/interfaces.ts | 11 + packages/plugin-cosmos/src/templates/index.ts | 51 +++++ .../cosmos-ibc-swap-action-service.test.ts | 113 ++++++++++ 16 files changed, 553 insertions(+), 12 deletions(-) create mode 100644 packages/plugin-cosmos/src/actions/ibc-swap/index.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-swap/schema.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts create mode 100644 packages/plugin-cosmos/src/actions/ibc-swap/types.ts create mode 100644 packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts diff --git a/agent/package.json b/agent/package.json index eb4a23a2752..a249990ffaf 100644 --- a/agent/package.json +++ b/agent/package.json @@ -36,7 +36,7 @@ "@elizaos/plugin-binance": "workspace:*", "@elizaos/plugin-avail": "workspace:*", "@elizaos/plugin-bootstrap": "workspace:*", - "@ai16z/plugin-cosmos": "workspace:*", + "@elizaos/plugin-cosmos": "workspace:*", "@elizaos/plugin-intiface": "workspace:*", "@elizaos/plugin-coinbase": "workspace:*", "@elizaos/plugin-conflux": "workspace:*", @@ -80,4 +80,4 @@ "ts-node": "10.9.2", "tsup": "8.3.5" } -} \ No newline at end of file +} diff --git a/agent/src/index.ts b/agent/src/index.ts index cae0789869b..2bad303c888 100644 --- a/agent/src/index.ts +++ b/agent/src/index.ts @@ -51,7 +51,7 @@ import { confluxPlugin } from "@elizaos/plugin-conflux"; import { cronosZkEVMPlugin } from "@elizaos/plugin-cronoszkevm"; import { echoChambersPlugin } from "@elizaos/plugin-echochambers"; import { evmPlugin } from "@elizaos/plugin-evm"; -import { createCosmosPlugin } from "@ai16z/plugin-cosmos"; +import { createCosmosPlugin } from "@elizaos/plugin-cosmos"; import { flowPlugin } from "@elizaos/plugin-flow"; import { fuelPlugin } from "@elizaos/plugin-fuel"; import { genLayerPlugin } from "@elizaos/plugin-genlayer"; diff --git a/packages/plugin-cosmos/package.json b/packages/plugin-cosmos/package.json index 90004ab76bd..f3d0699c415 100644 --- a/packages/plugin-cosmos/package.json +++ b/packages/plugin-cosmos/package.json @@ -1,5 +1,5 @@ { - "name": "@ai16z/plugin-cosmos", + "name": "@elizaos/plugin-cosmos", "version": "1.0.0", "main": "dist/index.js", "type": "module", diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/index.ts b/packages/plugin-cosmos/src/actions/ibc-swap/index.ts new file mode 100644 index 00000000000..ba92f9157c9 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-swap/index.ts @@ -0,0 +1,198 @@ +import { + composeContext, + generateObjectDeprecated, + HandlerCallback, + IAgentRuntime, + Memory, + ModelClass, + State +} from "@elizaos/core"; + +import { initWalletChainsData } from "../../providers/wallet/utils"; +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"; + +export const createIBCSwapAction = ( + pluginOptions: ICosmosPluginOptions +) => ({ + name: "COSMOS_IBC_SWAP", + description: "Swaps tokens between addresses on cosmos chains", + handler: async ( + _runtime: IAgentRuntime, + _message: Memory, + state: State, + _options: { [key: string]: unknown }, + _callback?: HandlerCallback + ) => { + const cosmosIBCTransferContext = composeContext({ + state: state, + template: cosmosIBCSwapTemplate, + templatingEngine: "handlebars", + }); + + const cosmosIBCSwapContent = await generateObjectDeprecated({ + runtime: _runtime, + context: cosmosIBCTransferContext, + modelClass: ModelClass.SMALL, + }); + + const paramOptions: IBCSwapActionParams = { + fromChainName: cosmosIBCSwapContent.fromChainName, + fromTokenSymbol: cosmosIBCSwapContent.fromTokenSymbol, + fromTokenAmount: cosmosIBCSwapContent.fromTokenAmount, + toTokenSymbol: cosmosIBCSwapContent.toTokenSymbol, + toChainName: cosmosIBCSwapContent.toChainName, + }; + + console.log('ParamOptions: ',JSON.stringify(paramOptions, null, 2)); + + try { + const walletProvider: ICosmosWalletChains = + await initWalletChainsData(_runtime); + + const action = new IBCSwapAction(walletProvider) + + const customAssets = (pluginOptions?.customChainData ?? []).map( + (chainData) => chainData.assets + ); + + const transferResp = await action.execute( + paramOptions, + customAssets, + _callback + ); + + 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}`, + 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 }, + }); + } + + 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; + } + }, + template: cosmosTransferTemplate, + validate: async (runtime: IAgentRuntime) => { + const mnemonic = runtime.getSetting("COSMOS_RECOVERY_PHRASE"); + const availableChains = runtime.getSetting("COSMOS_AVAILABLE_CHAINS"); + const availableChainsArray = availableChains?.split(","); + + return !(mnemonic && availableChains && availableChainsArray.length); + }, + examples: [ + [ + { + user: "{{user1}}", + content: { + text: "Swap {{0.0001 ATOM}} from {{cosmoshub}} to {{OM}} on {{mantrachain1}}", + action: "COSMOS_IBC_SWAP", + }, + }, + { + user: "{{user2}}", + content: { + text: "Do you confirm the swap?", + action: "COSMOS_IBC_SWAP", + }, + }, + { + user: "{{user1}}", + content: { + text: "Yes", + action: "COSMOS_IBC_SWAP", + }, + }, + { + user: "{{user2}}", + content: { + text: "Starting swap transaction. Keep in mind that it might take couple of minutes", + action: "COSMOS_IBC_SWAP", + }, + }, + ], + [ + { + user: "{{user1}}", + content: { + text: "Swap {{0.0001 OM}} from {{mantrachain}} to {{OSMO}} on {{osmosis}}", + action: "COSMOS_IBC_SWAP", + }, + }, + { + user: "{{user2}}", + content: { + text: "Do you confirm the swap?", + action: "COSMOS_IBC_SWAP", + }, + }, + { + user: "{{user1}}", + content: { + text: "Yes", + action: "COSMOS_IBC_SWAP", + }, + }, + { + user: "{{user2}}", + content: { + text: "Starting swap transaction. Keep in mind that it might take couple of minutes", + action: "COSMOS_IBC_SWAP", + }, + }, + ], + ], + similes: [ + "COSMOS_SWAP", + "COSMOS_SWAP_IBC", + ], +}); diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts b/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts new file mode 100644 index 00000000000..878a3f4cc19 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts @@ -0,0 +1,9 @@ +import {z} from "zod"; + +export const IBCSwapParamsSchema = z.object({ + fromChainName: z.string(), + fromTokenSymbol: z.string(), + fromTokenAmount: z.string(), + toTokenSymbol: z.string(), + toChainName: z.string(), +}); diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts new file mode 100644 index 00000000000..4ee93a0a6aa --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts @@ -0,0 +1,143 @@ +import {assets, chains } from "chain-registry"; +import { + ICosmosActionService, ICosmosPluginCustomChainData, + ICosmosSwap, + ICosmosWalletChains, +} from "../../../shared/interfaces.ts"; +import { IBCSwapActionParams } from "../types.ts"; +import { + getAssetBySymbol, + getChainByChainName, + getChainNameByChainId +} from "@chain-registry/utils"; +import { getAvailableAssets } from "../../../shared/helpers/cosmos-assets.ts"; +import {HandlerCallback} from "@elizaos/core"; + +export class IBCSwapAction implements ICosmosActionService { + constructor(private cosmosWalletChains: ICosmosWalletChains) { + this.cosmosWalletChains = cosmosWalletChains; + } + + async execute( + params: IBCSwapActionParams, + customChainAssets?: ICosmosPluginCustomChainData["assets"][], + _callback?: HandlerCallback + ): Promise { + const fromChain = getChainByChainName(chains, params.fromChainName); + const toChain = getChainByChainName(chains, params.toChainName); + + const availableAssets = getAvailableAssets(assets, customChainAssets) + + const denomFrom = getAssetBySymbol( + availableAssets, + params.fromTokenSymbol, + params.fromChainName + ); + + const denomTo = getAssetBySymbol( + availableAssets, + params.toTokenSymbol, + params.toChainName + ); + + 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 skipClient = this.cosmosWalletChains.getSkipClient( + params.fromChainName + ); + + + const route = await skipClient.route({ + smartSwapOptions: {}, + amountIn: params.fromTokenAmount, + sourceAssetDenom: denomFrom.base, + sourceAssetChainID: fromChain.chain_id, + destAssetDenom: denomTo.base, + 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 !! + const userAddresses = await Promise.all( + route.requiredChainAddresses.map(async (chainID) => { + const chainName = getChainNameByChainId(chains, chainID); + return { + chainID, + address: + await this.cosmosWalletChains.getWalletAddress( + chainName + ), + }; + }) + ); + + // 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}`, + }); + } + + let result: ICosmosSwap; + + await skipClient.executeRoute({ + route, + userAddresses, + onTransactionCompleted: async (chainID, txHash, status) => { + console.log( + `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, + }; + }, + }); + + return result; + } +} diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/types.ts b/packages/plugin-cosmos/src/actions/ibc-swap/types.ts new file mode 100644 index 00000000000..6cfee15375f --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-swap/types.ts @@ -0,0 +1,4 @@ +import {z} from "zod"; +import {IBCSwapParamsSchema} from "./schema.ts"; + +export type IBCSwapActionParams = z.infer; diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts index 687d315f11f..6998112ac79 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -6,7 +6,7 @@ import { Memory, ModelClass, State, -} from "@ai16z/eliza"; +} from "@elizaos/core"; import { initWalletChainsData } from "../../providers/wallet/utils"; import { cosmosIBCTransferTemplate, diff --git a/packages/plugin-cosmos/src/actions/transfer/index.ts b/packages/plugin-cosmos/src/actions/transfer/index.ts index cf1049a4d62..efb9051b129 100644 --- a/packages/plugin-cosmos/src/actions/transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/transfer/index.ts @@ -6,7 +6,7 @@ import { Memory, ModelClass, State, -} from "@ai16z/eliza"; +} from "@elizaos/core"; import { initWalletChainsData } from "../../providers/wallet/utils"; import { cosmosTransferTemplate } from "../../templates"; import { CosmosTransferActionService } from "./services/cosmos-transfer-action-service"; diff --git a/packages/plugin-cosmos/src/index.ts b/packages/plugin-cosmos/src/index.ts index 8e3eeb9e276..51ba4016549 100644 --- a/packages/plugin-cosmos/src/index.ts +++ b/packages/plugin-cosmos/src/index.ts @@ -1,7 +1,8 @@ import { createTransferAction } from "./actions/transfer"; -import type { Plugin } from "@ai16z/eliza"; +import type { Plugin } from "@elizaos/core"; import { createCosmosWalletProvider } from "./providers/wallet"; import { ICosmosPluginOptions } from "./shared/interfaces"; +import {createIBCSwapAction} from "./actions/ibc-swap"; export const createCosmosPlugin = ( pluginOptions?: ICosmosPluginOptions @@ -11,7 +12,7 @@ export const createCosmosPlugin = ( providers: [createCosmosWalletProvider(pluginOptions)], evaluators: [], services: [], - actions: [createTransferAction(pluginOptions)], + actions: [createTransferAction(pluginOptions), createIBCSwapAction(pluginOptions)], }); export default createCosmosPlugin; diff --git a/packages/plugin-cosmos/src/providers/wallet/index.ts b/packages/plugin-cosmos/src/providers/wallet/index.ts index 04b73bf2bb8..a1f3b9dfeaf 100644 --- a/packages/plugin-cosmos/src/providers/wallet/index.ts +++ b/packages/plugin-cosmos/src/providers/wallet/index.ts @@ -1,4 +1,4 @@ -import { IAgentRuntime } from "@ai16z/eliza"; +import { IAgentRuntime } from "@elizaos/core"; import { convertBaseUnitToDisplayUnit, getSymbolByDenom, diff --git a/packages/plugin-cosmos/src/providers/wallet/utils.ts b/packages/plugin-cosmos/src/providers/wallet/utils.ts index 9c3dac992cb..9163e16bb39 100644 --- a/packages/plugin-cosmos/src/providers/wallet/utils.ts +++ b/packages/plugin-cosmos/src/providers/wallet/utils.ts @@ -1,4 +1,4 @@ -import { IAgentRuntime } from "@ai16z/eliza"; +import { IAgentRuntime } from "@elizaos/core"; import { CosmosWalletChains } from "../../shared/entities/cosmos-wallet-chains-data"; export const initWalletChainsData = async (runtime: IAgentRuntime) => { diff --git a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts index 3c4cc70f9a4..08dfaeecccd 100644 --- a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts +++ b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts @@ -65,7 +65,12 @@ export class CosmosWalletChains implements ICosmosWalletChains { } public async getWalletAddress(chainName: string) { - return await this.walletChainsData[chainName].wallet.getWalletAddress(); + const chainWalletsForGivenChain = this.walletChainsData[chainName]; + if (!chainWalletsForGivenChain) { + throw new Error("Invalid chain name"); + } + + return await chainWalletsForGivenChain.wallet.getWalletAddress(); } public getSigningCosmWasmClient(chainName: string) { @@ -73,7 +78,13 @@ export class CosmosWalletChains implements ICosmosWalletChains { } public getSkipClient(chainName: string): SkipClient { - return this.walletChainsData[chainName].skipClient; + const chainWalletsForGivenChain = this.walletChainsData[chainName]; + + if (!chainWalletsForGivenChain) { + throw new Error("Invalid chain name"); + } + + return chainWalletsForGivenChain.skipClient; } public async getUserAddress(chainName: string): Promise { diff --git a/packages/plugin-cosmos/src/shared/interfaces.ts b/packages/plugin-cosmos/src/shared/interfaces.ts index 2f4dc2438c3..3d7756fdb96 100644 --- a/packages/plugin-cosmos/src/shared/interfaces.ts +++ b/packages/plugin-cosmos/src/shared/interfaces.ts @@ -23,6 +23,17 @@ export interface ICosmosTransaction { gasPaid?: number; } +export interface ICosmosSwap { + fromChainName: string; + fromTokenSymbol: string; + fromTokenAmount: string; + toTokenSymbol: string; + toTokenAmount: string; + toChainName: string; + txHash: string; + gasPaid: number; +} + export interface ICosmosWallet { directSecp256k1HdWallet: DirectSecp256k1HdWallet; diff --git a/packages/plugin-cosmos/src/templates/index.ts b/packages/plugin-cosmos/src/templates/index.ts index 136a5dc78d1..0773ba16246 100644 --- a/packages/plugin-cosmos/src/templates/index.ts +++ b/packages/plugin-cosmos/src/templates/index.ts @@ -84,3 +84,54 @@ Example response for the input: "Make an IBC transfer of 0.0001 ATOM to osmo1pcn Now respond with a JSON markdown block containing only the extracted values. `; + + + +export const cosmosIBCSwapTemplate = `Given the recent messages and cosmos wallet information below: +{{recentMessages}} +{{walletInfo}} +Extract the following information about the requested IBC swap: + +1. **fromChainName**: + - Identify the source chain mentioned in the instruction (e.g., cosmoshub, osmosis, axelar). + - Provide this as a string. + +2. **fromTokenSymbol**: + - The symbol must be a string representing the token's display denomination (e.g., "ATOM", "OSMO", etc.). + +3. **fromTokenAmount**: + - Extract only the numeric value from the instruction. + - The value must be a string representing the amount in the display denomination (e.g., "0.0001" for ATOM, OSMO, etc.). Do not include the symbol. + +4. **toChainName**: + - Identify the target chain mentioned in the instruction (e.g., cosmoshub, osmosis, axelar). + - Provide this as a string. + +5. **toTokenSymbol**: + - The symbol must be a string representing the result token's display denomination (e.g., "OM", "ATOM", etc.). + +Respond with a JSON markdown block containing only the extracted values. All fields are required: +\`\`\`json +{ + "fromChainName": string, // Source chain from which tokens will be taken to swap (String). + "fromTokenSymbol": string, // Symbol of token to be swapped (String). + "fromTokenAmount": string, // Amount of tokens to be swapped (String). + "toChainName": string, // Name of chain on which result token is hosted (String). + "toTokenSymbol": string // Symbol of result token (String). +} +\`\`\` + +Example response for the input: "Swap {{1}} {{ATOM}} from {{cosmoshub}} to {{OM}} on {{mantrachain}}", the response should be: +\`\`\`json +{ + "fromChainName": "cosmoshub", + "fromTokenSymbol": "ATOM", + "fromTokenAmount": "1", + "toChainName": "mantrachain", + "toTokenSymbol": "OM" +} +\`\`\` + +Now respond with a JSON markdown block containing only the extracted values. +`; + diff --git a/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts b/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts new file mode 100644 index 00000000000..3c32ebac70f --- /dev/null +++ b/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts @@ -0,0 +1,113 @@ +import {describe, it, expect, vi, beforeEach, Mock} from "vitest"; +import {IBCSwapAction} from "../actions/ibc-swap/services/ibc-swap-action-service.ts"; +import {HandlerCallback} from "@elizaos/core"; +import {getAssetBySymbol} from "@chain-registry/utils"; + +vi.mock("@cosmjs/cosmwasm-stargate", () => ({ + SigningCosmWasmClient: { + connectWithSigner: vi.fn(), + }, +})); + +vi.mock("@chain-registry/utils", () => ({ + getAssetBySymbol: vi.fn(), + getChainByChainName: vi.fn((_, chainName: string) => { + if (chainName === "source-chain") return { chain_id: "source-chain-id" }; + return { chain_id: "target-chain-id" }; + }), + getChainNameByChainId: vi.fn((_, chainId: string) => { + if (chainId === "source-chain-id") return "source-chain"; + return "target-chain"; + }), + convertDisplayUnitToBaseUnit: vi.fn(() => "1"), + getChainByChainId: vi.fn(() => ({ chainId: "target-chain-id" })), +})); + + +describe("IBCSwapAction", () => { + const mockWalletChains = { + getWalletAddress: vi.fn(), + getSkipClient: vi.fn(), + walletChainsData: {}, + getSigningCosmWasmClient: vi.fn(), + }; + + const mockSwapDenomProvider = vi.fn(); + + const mockSkipClient = { + route: vi.fn(), + executeRoute: vi.fn(), + }; + + const params = { + fromChainName: "source-chain", + fromTokenSymbol: "fromTokenSymbol", + fromTokenAmount: "1000", + toChainName: "target-chain", + toTokenSymbol: "toTokenSymbol", + }; + + const _callback: Mock = vi.fn(); + + const customChainAssets = []; + + beforeEach(() => { + vi.clearAllMocks(); + mockWalletChains.getSkipClient.mockReturnValue(mockSkipClient); + }); + + it("should complete", async () => { + mockWalletChains.getWalletAddress('source-chain').mockReturnValue('source-chain-address'); + mockWalletChains.getWalletAddress('target-chain').mockReturnValue('target-chain-address'); + const ibcSwapAction = new IBCSwapAction(mockWalletChains); + + (getAssetBySymbol as Mock).mockImplementationOnce((symbol: string) => { + if (symbol === "fromTokenSymbol") { + return { asset: { base: "fromTokenDenom"} }; + } + if (symbol === "toTokenSymbol") { + return { asset: { base: "toTokenDenom"} }; + } + }); + + (mockSkipClient.route as Mock).mockReturnValue({ + estimatedAmountOut: "123", + estimatedFees: "1", + estimatedRouteDurationSeconds: "1", + }) + + expect(mockSkipClient.route).toBeCalledWith( + { + smartSwapOptions: {}, + amountIn: params.fromTokenAmount, + sourceAssetDenom: "fromTokenDenom", + sourceAssetChainID: "source-chain-id", + destAssetDenom: "toTokenDenom", + destAssetChainID: "target-chain-id", + } + ); + + expect(_callback).toBeCalledWith( + { + text: `Expected swap result: 123 ${params.toTokenSymbol}, \nEstimated Fee: 1. \nEstimated time: 1`, + } + ) + + await expect( + await ibcSwapAction.execute( + params, + customChainAssets, + _callback + ) + ).resolves.toEqual({ + fromChainName: params.fromChainName, + fromTokenAmount: params.fromTokenAmount, + fromTokenSymbol: params.fromTokenSymbol, + gasPaid: 0, + toChainName: params.toChainName, + toTokenAmount: "ile??", //todo: get exchange result + toTokenSymbol: params.toTokenSymbol, + txHash: undefined, + }); + }); +}); From 131e78370aff377f63d1d75323ce3f57947f2ccf Mon Sep 17 00:00:00 2001 From: stanislawkurzypBD Date: Fri, 10 Jan 2025 15:49:06 +0100 Subject: [PATCH 09/12] ELIZAAI-18 Added error handling, improved prompt data extracting, fixed denom fetching --- .../src/actions/ibc-swap/index.ts | 90 ++++++++--------- .../src/actions/ibc-swap/schema.ts | 2 + .../services/ibc-swap-action-service.ts | 97 +++++++------------ .../src/actions/ibc-swap/services/utils.ts | 35 +++++++ .../entities/cosmos-wallet-chains-data.ts | 2 +- .../plugin-cosmos/src/shared/interfaces.ts | 2 - packages/plugin-cosmos/src/templates/index.ts | 53 +++++++++- 7 files changed, 168 insertions(+), 113 deletions(-) create mode 100644 packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/index.ts b/packages/plugin-cosmos/src/actions/ibc-swap/index.ts index ba92f9157c9..9f8bc3b3238 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/index.ts @@ -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, @@ -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 @@ -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; } }, @@ -191,8 +186,5 @@ export const createIBCSwapAction = ( }, ], ], - similes: [ - "COSMOS_SWAP", - "COSMOS_SWAP_IBC", - ], + similes: ["COSMOS_SWAP", "COSMOS_SWAP_IBC"], }); diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts b/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts index 878a3f4cc19..0b167207993 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/schema.ts @@ -6,4 +6,6 @@ export const IBCSwapParamsSchema = z.object({ fromTokenAmount: z.string(), toTokenSymbol: z.string(), toChainName: z.string(), + toTokenDenom: z.string(), + fromTokenDenom: z.string(), }); diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts index 4ee93a0a6aa..a1488fc360c 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts @@ -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) { @@ -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); @@ -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}`, @@ -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, }; diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts new file mode 100644 index 00000000000..5bc97427df3 --- /dev/null +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts @@ -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(); +}; diff --git a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts index cf713cd9856..334dc8197b7 100644 --- a/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts +++ b/packages/plugin-cosmos/src/shared/entities/cosmos-wallet-chains-data.ts @@ -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(); diff --git a/packages/plugin-cosmos/src/shared/interfaces.ts b/packages/plugin-cosmos/src/shared/interfaces.ts index 71cc8ff7670..4b2462dc42b 100644 --- a/packages/plugin-cosmos/src/shared/interfaces.ts +++ b/packages/plugin-cosmos/src/shared/interfaces.ts @@ -28,10 +28,8 @@ export interface ICosmosSwap { fromTokenSymbol: string; fromTokenAmount: string; toTokenSymbol: string; - toTokenAmount: string; toChainName: string; txHash: string; - gasPaid: number; } export interface ICosmosWallet { diff --git a/packages/plugin-cosmos/src/templates/index.ts b/packages/plugin-cosmos/src/templates/index.ts index 0773ba16246..a3d8b6b088e 100644 --- a/packages/plugin-cosmos/src/templates/index.ts +++ b/packages/plugin-cosmos/src/templates/index.ts @@ -110,6 +110,14 @@ Extract the following information about the requested IBC swap: 5. **toTokenSymbol**: - The symbol must be a string representing the result token's display denomination (e.g., "OM", "ATOM", etc.). +6. **toTokenDenom**: + - optional parameter, if present must be a string. (uom, uatom, usomo, ibc/53046FFF6CAD109D8F9B2C7C9913853AD241928CD05CDDE419343D176025DA74 or other ibc/ values) + +7. **fromTokenDenom**: + - optional parameter, if present must be a string. (uom, uatom, usomo, ibc/53046FFF6CAD109D8F9B2C7C9913853AD241928CD05CDDE419343D176025DA74 or other ibc/ values) + +Keep in mind that toTokenDenom and fromTokenDenom are optional parameters and + Respond with a JSON markdown block containing only the extracted values. All fields are required: \`\`\`json { @@ -118,6 +126,8 @@ Respond with a JSON markdown block containing only the extracted values. All fie "fromTokenAmount": string, // Amount of tokens to be swapped (String). "toChainName": string, // Name of chain on which result token is hosted (String). "toTokenSymbol": string // Symbol of result token (String). + "fromTokenDenom": string // denom of token to be swapped (String). Optional, might not be present. + "toTokenDenom": string // denom of result token (String). Optional, might not be present. } \`\`\` @@ -127,8 +137,49 @@ Example response for the input: "Swap {{1}} {{ATOM}} from {{cosmoshub}} to {{OM} "fromChainName": "cosmoshub", "fromTokenSymbol": "ATOM", "fromTokenAmount": "1", + "fromTokenDenom": null, + "toChainName": "mantrachain", + "toTokenSymbol": "OM", + "fromTokenDenom": null +} +\`\`\` + + +Example response for the input: "Swap {{1}} {{ATOM}} with denom {{uatom}} from {{cosmoshub}} to {{OM}} on {{mantrachain}}", the response should be: +\`\`\`json +{ + "fromChainName": "cosmoshub", + "fromTokenSymbol": "ATOM", + "fromTokenAmount": "1", + "fromTokenDenom": "uatom", + "toChainName": "mantrachain", + "toTokenSymbol": "OM", + "fromTokenDenom": null +} +\`\`\` + +Example response for the input: "Swap {{1}} {{ATOM}} with denom {{uatom}} from {{cosmoshub}} to {{OM}} (denom: {{ibc/53046FFF6CAD109D8F9B2C7C9913853AD241928CD05CDDE419343D176025DA74}} ) on {{mantrachain}}", the response should be: +\`\`\`json +{ + "fromChainName": "cosmoshub", + "fromTokenSymbol": "ATOM", + "fromTokenAmount": "1", + "fromTokenDenom": "uatom", "toChainName": "mantrachain", - "toTokenSymbol": "OM" + "toTokenSymbol": "OM", + "toTokenDenom": "ibc/53046FFF6CAD109D8F9B2C7C9913853AD241928CD05CDDE419343D176025DA74" +} +\`\`\` + +Example response for the input: "Swap {{100}} {{USDC}} with denom {{uusdc}} from {{axelar}} to {{ATOM}} on {{cosmoshub}}", the response should be: +\`\`\`json +{ + "fromChainName": "axelar", + "fromTokenSymbol": "USDC", + "fromTokenAmount": "1", + "fromTokenDenom": "uusdc", + "toChainName": "cosmoshub", + "toTokenSymbol": "ATOM", } \`\`\` From 291fe6f6cb3f8095507201ea7fe61ca75070c93a Mon Sep 17 00:00:00 2001 From: stanislawkurzypBD Date: Fri, 10 Jan 2025 17:01:48 +0100 Subject: [PATCH 10/12] ELIZAAI-18 Unit tests added, data extracting template adjusted and character directives updated --- characters/cosmosHelper.character.json | 27 +- .../src/actions/ibc-swap/index.ts | 11 +- .../services/ibc-swap-action-service.ts | 3 +- .../services/{utils.ts => ibc-swap-utils.ts} | 6 +- .../plugin-cosmos/src/shared/interfaces.ts | 3 +- packages/plugin-cosmos/src/templates/index.ts | 7 +- .../cosmos-ibc-swap-action-service.test.ts | 279 +++++++++++++++--- ...cosmos-ibc-transfer-action-service.test.ts | 2 +- .../src/tests/ibc-swap-utils.test.ts | 66 +++++ 9 files changed, 339 insertions(+), 65 deletions(-) rename packages/plugin-cosmos/src/actions/ibc-swap/services/{utils.ts => ibc-swap-utils.ts} (92%) create mode 100644 packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts diff --git a/characters/cosmosHelper.character.json b/characters/cosmosHelper.character.json index 5352eb02c14..e74d93d4be5 100644 --- a/characters/cosmosHelper.character.json +++ b/characters/cosmosHelper.character.json @@ -7,7 +7,11 @@ "model": "en_US-male-medium" }, "chains": { - "cosmos": ["axelar", "carbon", "mantrachaintestnet2"] + "cosmos": [ + "axelar", + "carbon", + "mantrachaintestnet2" + ] } }, "plugins": [], @@ -27,13 +31,16 @@ "Knows what actions should he call for token transfer, swapping or bridging", "Knows that users might want to do specific actions multiple times and should help them by doing it again.", "Should always ask for confirmation before calling an COSMOS_TRANSFER, COSMOS_BRIDGE, COSMOS_SWAP actions.", + "Should ask for confirmation ONCE and perform action after getting it. If user wants to change sth in data for transfer, should do it and ask again for confirmation of new data.", "Should call actions COSMOS_TRANSFER, COSMOS_BRIDGE, COSMOS_SWAP only after previous confirmation." ], "messageExamples": [ [ { "user": "{{user1}}", - "content": { "text": "Show my balances of my wallet on {{mantrachaintestnet2}}" } + "content": { + "text": "Show my balances of my wallet on {{mantrachaintestnet2}}" + } }, { "user": "CosmosHelper", @@ -45,7 +52,9 @@ [ { "user": "{{user1}}", - "content": { "text": "How does IBC work?" } + "content": { + "text": "How does IBC work?" + } }, { "user": "CosmosHelper", @@ -57,7 +66,9 @@ [ { "user": "{{user1}}", - "content": { "text": "What is CosmWasm?" } + "content": { + "text": "What is CosmWasm?" + } }, { "user": "CosmosHelper", @@ -69,7 +80,9 @@ [ { "user": "{{user1}}", - "content": { "text": "Can you help me transfer tokens?" } + "content": { + "text": "Can you help me transfer tokens?" + } }, { "user": "CosmosHelper", @@ -81,7 +94,9 @@ [ { "user": "{{user1}}", - "content": { "text": "Make transfer 0.0001 OM to mantra13248w8dtnn07sxc3gq4l3ts4rvfyat6fks0ecj on mantrachaintestnet2" } + "content": { + "text": "Make transfer 0.0001 OM to mantra13248w8dtnn07sxc3gq4l3ts4rvfyat6fks0ecj on mantrachaintestnet2" + } }, { "user": "CosmosHelper", diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/index.ts b/packages/plugin-cosmos/src/actions/ibc-swap/index.ts index 9f8bc3b3238..0d2e79db364 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/index.ts @@ -16,7 +16,7 @@ import type { } from "../../shared/interfaces"; import { IBCSwapActionParams } from "./types.ts"; import { IBCSwapAction } from "./services/ibc-swap-action-service.ts"; -import { prepareAmbiguityErrorMessage } from "./services/utils.ts"; +import { prepareAmbiguityErrorMessage } from "./services/ibc-swap-utils.ts"; export const createIBCSwapAction = (pluginOptions: ICosmosPluginOptions) => ({ name: "COSMOS_IBC_SWAP", @@ -72,10 +72,15 @@ export const createIBCSwapAction = (pluginOptions: ICosmosPluginOptions) => ({ ); if (_callback) { + const text = + transferResp.status === "STATE_COMPLETED_SUCCESS" + ? `Successfully swapped ${transferResp.fromTokenAmount} ${transferResp.fromTokenSymbol} tokens to ${transferResp.toTokenSymbol} on chain ${transferResp.toChainName}.\nTransaction Hash: ${transferResp.txHash}` + : `Error occured swapping ${transferResp.fromTokenAmount} ${transferResp.fromTokenSymbol} tokens to ${transferResp.toTokenSymbol} on chain ${transferResp.toChainName}.\nTransaction Hash: ${transferResp.txHash}, try again`; await _callback({ - text: `Successfully swapped ${transferResp.fromTokenAmount} ${transferResp.fromTokenSymbol} tokens to ${transferResp.toTokenSymbol} on chain ${transferResp.toChainName}.\nTransaction Hash: ${transferResp.txHash}`, + text: text, content: { - success: true, + success: + transferResp.status === "STATE_COMPLETED_SUCCESS", hash: transferResp.txHash, fromTokenAmount: paramOptions.fromTokenAmount, fromToken: paramOptions.fromTokenSymbol, diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts index a1488fc360c..ac255d10177 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts @@ -14,7 +14,7 @@ import { } from "@chain-registry/utils"; import { getAvailableAssets } from "../../../shared/helpers/cosmos-assets.ts"; import { HandlerCallback } from "@elizaos/core"; -import { calculateAmountInDenomFromDisplayUnit } from "./utils.ts"; +import { calculateAmountInDenomFromDisplayUnit } from "./ibc-swap-utils.ts"; export class IBCSwapAction implements ICosmosActionService { constructor(private cosmosWalletChains: ICosmosWalletChains) { @@ -105,6 +105,7 @@ export class IBCSwapAction implements ICosmosActionService { ); result = { + status: status.state, fromChainName: params.fromChainName, fromTokenAmount: params.fromTokenAmount, fromTokenSymbol: params.fromTokenSymbol, diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts similarity index 92% rename from packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts rename to packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts index 5bc97427df3..4b8df96aae8 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/services/utils.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts @@ -23,13 +23,13 @@ export const prepareAmbiguityErrorMessage = ( /** * Calculates amount passed in display unit * @param tokenAmount - * @param exponet + * @param exponent */ export const calculateAmountInDenomFromDisplayUnit = ( tokenAmount: string, - exponet: number + exponent: number ) => { return new BigNumber(tokenAmount) - .multipliedBy(new BigNumber(10).pow(exponet)) + .multipliedBy(new BigNumber(10).pow(exponent)) .toString(); }; diff --git a/packages/plugin-cosmos/src/shared/interfaces.ts b/packages/plugin-cosmos/src/shared/interfaces.ts index 4b2462dc42b..5fefe43e2c9 100644 --- a/packages/plugin-cosmos/src/shared/interfaces.ts +++ b/packages/plugin-cosmos/src/shared/interfaces.ts @@ -1,7 +1,7 @@ import type { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import type { Coin, DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; import type { assets, chains } from "chain-registry"; -import { SkipClient } from "@skip-go/client"; +import {SkipClient, StatusState} from "@skip-go/client"; export interface ICosmosPluginCustomChainData { chainData: (typeof chains)[number]; @@ -24,6 +24,7 @@ export interface ICosmosTransaction { } export interface ICosmosSwap { + status: StatusState; fromChainName: string; fromTokenSymbol: string; fromTokenAmount: string; diff --git a/packages/plugin-cosmos/src/templates/index.ts b/packages/plugin-cosmos/src/templates/index.ts index a3d8b6b088e..c2d55fc6621 100644 --- a/packages/plugin-cosmos/src/templates/index.ts +++ b/packages/plugin-cosmos/src/templates/index.ts @@ -85,11 +85,11 @@ Example response for the input: "Make an IBC transfer of 0.0001 ATOM to osmo1pcn Now respond with a JSON markdown block containing only the extracted values. `; - - export const cosmosIBCSwapTemplate = `Given the recent messages and cosmos wallet information below: {{recentMessages}} {{walletInfo}} +Make sure that you extracted latest info about requested swap from recent messages. Espessialy if there was another one placed before. +Also the extracted info MUST match the confirmed by user data in latest prompt in which you asked for confirmation! Extract the following information about the requested IBC swap: 1. **fromChainName**: @@ -116,7 +116,7 @@ Extract the following information about the requested IBC swap: 7. **fromTokenDenom**: - optional parameter, if present must be a string. (uom, uatom, usomo, ibc/53046FFF6CAD109D8F9B2C7C9913853AD241928CD05CDDE419343D176025DA74 or other ibc/ values) -Keep in mind that toTokenDenom and fromTokenDenom are optional parameters and +Keep in mind that toTokenDenom and fromTokenDenom are optional parameters. Respond with a JSON markdown block containing only the extracted values. All fields are required: \`\`\`json @@ -185,4 +185,3 @@ Example response for the input: "Swap {{100}} {{USDC}} with denom {{uusdc}} from Now respond with a JSON markdown block containing only the extracted values. `; - diff --git a/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts b/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts index 3c32ebac70f..b87989e2856 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-ibc-swap-action-service.test.ts @@ -1,7 +1,8 @@ -import {describe, it, expect, vi, beforeEach, Mock} from "vitest"; -import {IBCSwapAction} from "../actions/ibc-swap/services/ibc-swap-action-service.ts"; -import {HandlerCallback} from "@elizaos/core"; -import {getAssetBySymbol} from "@chain-registry/utils"; +import { describe, it, expect, vi, beforeEach, Mock } from "vitest"; +import { IBCSwapAction } from "../actions/ibc-swap/services/ibc-swap-action-service.ts"; +import { HandlerCallback } from "@elizaos/core"; +import { getAssetBySymbol, getDenomBySymbol } from "@chain-registry/utils"; +import { Asset } from "@chain-registry/types"; vi.mock("@cosmjs/cosmwasm-stargate", () => ({ SigningCosmWasmClient: { @@ -12,18 +13,24 @@ vi.mock("@cosmjs/cosmwasm-stargate", () => ({ vi.mock("@chain-registry/utils", () => ({ getAssetBySymbol: vi.fn(), getChainByChainName: vi.fn((_, chainName: string) => { - if (chainName === "source-chain") return { chain_id: "source-chain-id" }; + if (chainName === "source-chain") + return { chain_id: "source-chain-id" }; return { chain_id: "target-chain-id" }; }), getChainNameByChainId: vi.fn((_, chainId: string) => { if (chainId === "source-chain-id") return "source-chain"; return "target-chain"; }), + getDenomBySymbol: vi.fn((_, symbol: string) => { + if (symbol === "fromTokenSymbol") return "fromTokenDenom"; + else if (symbol === "toTokenSymbol") return "toTokenDenom"; + }), + getExponentByDenom: vi.fn((_, denom: string) => { + if (denom === "fromTokenDenom") return "6"; + }), convertDisplayUnitToBaseUnit: vi.fn(() => "1"), getChainByChainId: vi.fn(() => ({ chainId: "target-chain-id" })), })); - - describe("IBCSwapAction", () => { const mockWalletChains = { getWalletAddress: vi.fn(), @@ -32,8 +39,6 @@ describe("IBCSwapAction", () => { getSigningCosmWasmClient: vi.fn(), }; - const mockSwapDenomProvider = vi.fn(); - const mockSkipClient = { route: vi.fn(), executeRoute: vi.fn(), @@ -53,61 +58,243 @@ describe("IBCSwapAction", () => { beforeEach(() => { vi.clearAllMocks(); - mockWalletChains.getSkipClient.mockReturnValue(mockSkipClient); + (mockWalletChains.getSkipClient as Mock).mockReturnValue( + mockSkipClient + ); }); it("should complete", async () => { - mockWalletChains.getWalletAddress('source-chain').mockReturnValue('source-chain-address'); - mockWalletChains.getWalletAddress('target-chain').mockReturnValue('target-chain-address'); - const ibcSwapAction = new IBCSwapAction(mockWalletChains); + // Mock wallet addresses + (mockWalletChains.getWalletAddress as Mock) + .mockImplementationOnce(() => "source-chain-address") + .mockImplementationOnce(() => "target-chain-address"); - (getAssetBySymbol as Mock).mockImplementationOnce((symbol: string) => { + // Mock route call, including `requiredChainAddresses` + (mockSkipClient.route as Mock).mockResolvedValue({ + estimatedAmountOut: "123", + estimatedFees: "1", + estimatedRouteDurationSeconds: "1", + requiredChainAddresses: ["source-chain-id", "target-chain-id"], + }); + + // Mock asset symbols + (getAssetBySymbol as Mock).mockImplementation((symbol: string) => { if (symbol === "fromTokenSymbol") { - return { asset: { base: "fromTokenDenom"} }; + return { asset: { base: "fromTokenDenom" } }; } if (symbol === "toTokenSymbol") { - return { asset: { base: "toTokenDenom"} }; + return { asset: { base: "toTokenDenom" } }; + } + return null; + }); + + // Mock `executeRoute` to simulate transaction completion + (mockSkipClient.executeRoute as Mock).mockImplementation( + ({ onTransactionCompleted }) => { + onTransactionCompleted("target-chain-id", "mockTxHash", { + state: "success", + }); } + ); + + const ibcSwapAction = new IBCSwapAction(mockWalletChains); + + // Execute the action + const result = await ibcSwapAction.execute( + params, + customChainAssets, + _callback + ); + + // Validate the route call + expect(mockSkipClient.route).toHaveBeenCalledWith({ + smartSwapOptions: {}, + amountOut: "1000000000", + sourceAssetDenom: "fromTokenDenom", + sourceAssetChainID: "source-chain-id", + destAssetDenom: "toTokenDenom", + destAssetChainID: "target-chain-id", + }); + + // Validate the callback + expect(_callback).toHaveBeenCalledWith({ + text: `Expected swap result: 123 ${params.toTokenSymbol}, \nEstimated Fee: 1. \nEstimated time: 1`, + }); + + // Validate the final result + expect(result).toEqual({ + fromChainName: params.fromChainName, + fromTokenAmount: params.fromTokenAmount, + fromTokenSymbol: params.fromTokenSymbol, + toChainName: params.toChainName, + toTokenSymbol: params.toTokenSymbol, + txHash: "mockTxHash", + status: "success", }); + }); - (mockSkipClient.route as Mock).mockReturnValue({ + it("should throw an error if route fails", async () => { + // Mock route failure + (mockSkipClient.route as Mock).mockRejectedValue( + new Error("Route failed") + ); + + const ibcSwapAction = new IBCSwapAction(mockWalletChains); + + await expect( + ibcSwapAction.execute(params, customChainAssets, _callback) + ).rejects.toThrow("Route failed"); + }); + + it("should handle transaction failure during execution", async () => { + // Mock successful route call + (mockSkipClient.route as Mock).mockResolvedValue({ estimatedAmountOut: "123", estimatedFees: "1", estimatedRouteDurationSeconds: "1", - }) + requiredChainAddresses: ["source-chain-id", "target-chain-id"], + }); - expect(mockSkipClient.route).toBeCalledWith( - { - smartSwapOptions: {}, - amountIn: params.fromTokenAmount, - sourceAssetDenom: "fromTokenDenom", - sourceAssetChainID: "source-chain-id", - destAssetDenom: "toTokenDenom", - destAssetChainID: "target-chain-id", + // Mock transaction failure + (mockSkipClient.executeRoute as Mock).mockImplementation( + ({ onTransactionCompleted }) => { + onTransactionCompleted("target-chain-id", "mockTxHash", { + state: "failure", + }); + } + ); + + const ibcSwapAction = new IBCSwapAction(mockWalletChains); + + const result = await ibcSwapAction.execute( + params, + customChainAssets, + _callback + ); + + // Validate the final result + expect(result).toEqual({ + status: "failure", + fromChainName: params.fromChainName, + fromTokenAmount: params.fromTokenAmount, + fromTokenSymbol: params.fromTokenSymbol, + toChainName: params.toChainName, + toTokenSymbol: params.toTokenSymbol, + txHash: "mockTxHash", + }); + + }); + + it("should complete without callback", async () => { + // Mock wallet addresses + (mockWalletChains.getWalletAddress as Mock) + .mockImplementationOnce(() => "source-chain-address") + .mockImplementationOnce(() => "target-chain-address"); + + // Mock route call + (mockSkipClient.route as Mock).mockResolvedValue({ + estimatedAmountOut: "123", + estimatedFees: "1", + estimatedRouteDurationSeconds: "1", + requiredChainAddresses: ["source-chain-id", "target-chain-id"], + }); + + // Mock transaction completion + (mockSkipClient.executeRoute as Mock).mockImplementation( + ({ onTransactionCompleted }) => { + onTransactionCompleted("target-chain-id", "mockTxHash", { + state: "success", + }); } ); - expect(_callback).toBeCalledWith( + const ibcSwapAction = new IBCSwapAction(mockWalletChains); + + // Execute without callback + const result = await ibcSwapAction.execute(params, customChainAssets); + + expect(result).toEqual({ + "status": "success", + fromChainName: params.fromChainName, + fromTokenAmount: params.fromTokenAmount, + fromTokenSymbol: params.fromTokenSymbol, + toChainName: params.toChainName, + toTokenSymbol: params.toTokenSymbol, + txHash: "mockTxHash", + }); + }); + + it("should use custom chain assets when provided", async () => { + const customAssets = [ { - text: `Expected swap result: 123 ${params.toTokenSymbol}, \nEstimated Fee: 1. \nEstimated time: 1`, + chain_name: "source-chain", + assets: [ + { + symbol: "fromTokenSymbol", + denom: "customFromDenom", + } as unknown as Asset, + ], + }, + { + chain_name: "target-chain", + assets: [ + { + symbol: "toTokenSymbol", + denom: "customToDenom", + } as unknown as Asset, + ], + }, + ]; + + (getDenomBySymbol as Mock).mockImplementation((assets, symbol) => { + if (symbol === "fromTokenSymbol") return "customFromDenom"; + if (symbol === "toTokenSymbol") return "customToDenom"; + }); + + // Mock route call + (mockSkipClient.route as Mock).mockResolvedValue({ + estimatedAmountOut: "123", + estimatedFees: "1", + estimatedRouteDurationSeconds: "1", + requiredChainAddresses: ["source-chain-id", "target-chain-id"], + }); + + // Mock transaction completion + (mockSkipClient.executeRoute as Mock).mockImplementation( + ({ onTransactionCompleted }) => { + onTransactionCompleted("target-chain-id", "mockTxHash", { + state: "success", + }); } - ) - - await expect( - await ibcSwapAction.execute( - params, - customChainAssets, - _callback - ) - ).resolves.toEqual({ - fromChainName: params.fromChainName, - fromTokenAmount: params.fromTokenAmount, - fromTokenSymbol: params.fromTokenSymbol, - gasPaid: 0, - toChainName: params.toChainName, - toTokenAmount: "ile??", //todo: get exchange result - toTokenSymbol: params.toTokenSymbol, - txHash: undefined, - }); + ); + + const ibcSwapAction = new IBCSwapAction(mockWalletChains); + + const result = await ibcSwapAction.execute( + params, + customAssets, + _callback + ); + + expect(result).toEqual({ + "status": "success", + fromChainName: params.fromChainName, + fromTokenAmount: params.fromTokenAmount, + fromTokenSymbol: params.fromTokenSymbol, + toChainName: params.toChainName, + toTokenSymbol: params.toTokenSymbol, + txHash: "mockTxHash", + }); + + expect(getDenomBySymbol).toHaveBeenCalledWith( + expect.anything(), + "fromTokenSymbol", + "source-chain" + ); + expect(getDenomBySymbol).toHaveBeenCalledWith( + expect.anything(), + "toTokenSymbol", + "target-chain" + ); }); }); diff --git a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts index 03311aca8d4..49f66aad195 100644 --- a/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts +++ b/packages/plugin-cosmos/src/tests/cosmos-ibc-transfer-action-service.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { IBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; // dostosuj ścieżkę do pliku +import { IBCTransferAction } from "../actions/ibc-transfer/services/ibc-transfer-action-service"; import { assets } from "chain-registry"; import * as CosmosAssetsHelpers from "../shared/helpers/cosmos-assets"; import { getAssetBySymbol } from "@chain-registry/utils"; diff --git a/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts b/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts new file mode 100644 index 00000000000..25f8bb6650c --- /dev/null +++ b/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, vi } from "vitest"; + +import { + calculateAmountInDenomFromDisplayUnit, + prepareAmbiguityErrorMessage, +} from "../actions/ibc-swap/services/ibc-swap-utils.ts"; + +vi.mock("chain-registry", () => ({ + assets: [ + { + chain_name: "test-chain", + assets: [ + { + symbol: "ATOM", + description: "Cosmos Hub token", + base: "atom-base", + }, + { + symbol: "ATOM", + description: "Wrapped Cosmos token", + base: "wrapped-atom-base", + }, + ], + }, + ], +})); + +describe("Utility Functions Tests", () => { + describe("prepareAmbiguityErrorMessage", () => { + it("should return an error message for ambiguous assets", () => { + const result = prepareAmbiguityErrorMessage("ATOM", "test-chain"); + + expect(result).toContain("Error occured. Swap was not performed."); + expect(result).toContain("ATOM"); + expect(result).toContain("test-chain"); + expect(result).toContain( + "Symbol: ATOM Desc: Cosmos Hub token Denom: atom-base" + ); + expect(result).toContain( + "Symbol: ATOM Desc: Wrapped Cosmos token Denom: wrapped-atom-base" + ); + }); + }); + + describe("calculateAmountInDenomFromDisplayUnit", () => { + it("should calculate the correct amount in denom", () => { + const result = calculateAmountInDenomFromDisplayUnit("1", 6); + expect(result).toBe("1000000"); + }); + + it("should handle decimal values correctly", () => { + const result = calculateAmountInDenomFromDisplayUnit("1.234", 6); + expect(result).toBe("1234000"); + }); + + it("should handle large exponent values correctly", () => { + const result = calculateAmountInDenomFromDisplayUnit("1", 18); + expect(result).toBe("1000000000000000000"); + }); + + it("should return 0 for a token amount of 0", () => { + const result = calculateAmountInDenomFromDisplayUnit("0", 6); + expect(result).toBe("0"); + }); + }); +}); From 7cac6e8804c71f46a31e98c6676a20dd5425aab0 Mon Sep 17 00:00:00 2001 From: stanislawkurzypBD Date: Mon, 13 Jan 2025 09:21:12 +0100 Subject: [PATCH 11/12] ELIZAAI-18 Switched to chain-registry function for fetching token exponent --- .../services/ibc-swap-action-service.ts | 8 +++--- .../ibc-swap/services/ibc-swap-utils.ts | 15 ----------- .../src/tests/ibc-swap-utils.test.ts | 27 +------------------ 3 files changed, 6 insertions(+), 44 deletions(-) diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts index ac255d10177..3bdf1ed8837 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-action-service.ts @@ -7,6 +7,7 @@ import { } from "../../../shared/interfaces.ts"; import { IBCSwapActionParams } from "../types.ts"; import { + convertDisplayUnitToBaseUnit, getChainByChainName, getChainNameByChainId, getDenomBySymbol, @@ -14,7 +15,6 @@ import { } from "@chain-registry/utils"; import { getAvailableAssets } from "../../../shared/helpers/cosmos-assets.ts"; import { HandlerCallback } from "@elizaos/core"; -import { calculateAmountInDenomFromDisplayUnit } from "./ibc-swap-utils.ts"; export class IBCSwapAction implements ICosmosActionService { constructor(private cosmosWalletChains: ICosmosWalletChains) { @@ -63,9 +63,11 @@ export class IBCSwapAction implements ICosmosActionService { const route = await skipClient.route({ smartSwapOptions: {}, - amountOut: calculateAmountInDenomFromDisplayUnit( + amountOut: convertDisplayUnitToBaseUnit( + availableAssets, + params.fromTokenSymbol, params.fromTokenAmount, - exponentFrom + params.fromChainName ), sourceAssetDenom: denomFrom, sourceAssetChainID: fromChain.chain_id, diff --git a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts index 4b8df96aae8..501ced5dada 100644 --- a/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts +++ b/packages/plugin-cosmos/src/actions/ibc-swap/services/ibc-swap-utils.ts @@ -1,5 +1,4 @@ import { assets } from "chain-registry"; -import BigNumber from "bignumber.js"; export const prepareAmbiguityErrorMessage = ( coinSymbol: string, @@ -19,17 +18,3 @@ export const prepareAmbiguityErrorMessage = ( 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 exponent - */ -export const calculateAmountInDenomFromDisplayUnit = ( - tokenAmount: string, - exponent: number -) => { - return new BigNumber(tokenAmount) - .multipliedBy(new BigNumber(10).pow(exponent)) - .toString(); -}; diff --git a/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts b/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts index 25f8bb6650c..d731c3d1b96 100644 --- a/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts +++ b/packages/plugin-cosmos/src/tests/ibc-swap-utils.test.ts @@ -1,9 +1,6 @@ import { describe, it, expect, vi } from "vitest"; -import { - calculateAmountInDenomFromDisplayUnit, - prepareAmbiguityErrorMessage, -} from "../actions/ibc-swap/services/ibc-swap-utils.ts"; +import { prepareAmbiguityErrorMessage } from "../actions/ibc-swap/services/ibc-swap-utils.ts"; vi.mock("chain-registry", () => ({ assets: [ @@ -41,26 +38,4 @@ describe("Utility Functions Tests", () => { ); }); }); - - describe("calculateAmountInDenomFromDisplayUnit", () => { - it("should calculate the correct amount in denom", () => { - const result = calculateAmountInDenomFromDisplayUnit("1", 6); - expect(result).toBe("1000000"); - }); - - it("should handle decimal values correctly", () => { - const result = calculateAmountInDenomFromDisplayUnit("1.234", 6); - expect(result).toBe("1234000"); - }); - - it("should handle large exponent values correctly", () => { - const result = calculateAmountInDenomFromDisplayUnit("1", 18); - expect(result).toBe("1000000000000000000"); - }); - - it("should return 0 for a token amount of 0", () => { - const result = calculateAmountInDenomFromDisplayUnit("0", 6); - expect(result).toBe("0"); - }); - }); }); From b14ba8873d6ae7fca08168922a7eb412c3cb7f51 Mon Sep 17 00:00:00 2001 From: stanislawkurzypBD Date: Mon, 13 Jan 2025 12:54:33 +0100 Subject: [PATCH 12/12] ELIZAAI-18 Updated Readme --- packages/plugin-cosmos/README.md | 72 ++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/packages/plugin-cosmos/README.md b/packages/plugin-cosmos/README.md index 610489e8638..71deea79ca6 100644 --- a/packages/plugin-cosmos/README.md +++ b/packages/plugin-cosmos/README.md @@ -144,6 +144,78 @@ Yes --- +### Token IBC Swap +This action allows swapping tokens between chains. The implementation of swapping is based on the Skip API and uses the @skip-go/client library. +To place transactions on chains, they must first be added to the env file. Specifically: +```env +COSMOS_AVAILABLE_CHAINS=osmosis,neutron,axelar,cosmoshub,terra2,pryzm +``` +Keep in mind that most swaps require intermediate chains. These chains must also be included in the env file. + +You can check which chains are supported by the Skip API and this plugin here: Skip API Documentation. + +The list below contains all supported chains extracted from the Skip API: +```env +COSMOS_AVAILABLE_CHAINS=terra2,quicksilver,coreum,regen,mars,passage,dhealth,lumnetwork,provenance,chihuahua,pryzm,fetchhub,comdex,kyve,xpla,umee,celestia,osmosis,empowerchain,migaloo,dymension,kujira,self,humans,gitopia,agoric,doravota,int3face,quasar,gravitybridge,kava,sifchain,seda,shentu,decentr,cronos,carbon,stride,haqq,jackal,omniflixhub,noble,sentinel,nolus,odin,assetmantle,dydx,beezee,axelar,nois,mantrachain,elys,crescent,synternet,xion,source,akash,sei,canto,teritori,stargaze,titan,aura,evmos,archway,gateway,cheqd,milkyway,nibiru,cosmoshub,ununifi,nyx,bitsong,cryptoorgchain,neutron,penumbra,terra,shido,sommelier,saga,secretnetwork,chain4energy,juno,andromeda,planq,lava,oraichain,injective,persistence,composable +``` + +#### Example prompts: + +##### Default flow +An example of the default flow of a swap request: + +1. User input: +``` + Swap 10 OSMO on osmosis to ATOM on cosmoshub +``` +2. Agent asks for confirmation: +``` +About to swap 10 OSMO on osmosis to ATAOM on cosmoshub. I would like to confirm the transaction details. Please confirm that you would like to proceed with the swap. +``` +3. User confiramation: +``` +confirming / yes +``` +4. Agent calls action and responds after completing: +``` +Successfuly swapped 10 OSMO tokens to ATOM on chain cosmoshub. +Transaction Hash: E84F36D6EEFAA6D7B70827F34EDAB83258BB86EFE33AEA1F4559D00B30CD3B50 +``` + +##### Special case handling +An example of a flow when there is more than one token with the same symbol on the destination chain (e.g., multiple USDC tokens): +1. User input: +``` +Swap 10 OSMO on osmosis to ATOM on cosmoshub +``` +2. Agent asks for confirmation: +``` +About to swap 10 OSMO on osmosis to ATAOM on cosmoshub. I would like to confirm the transaction details. Please confirm that you would like to proceed with the swap. +``` +3. User confirmation: +``` +confirming / yes +``` +4. Model response: +``` +I will now initiate the swap of 10 OSMO on the Osmosis chain to USDC on the Axelar chain. Please wait for the transaction to complete. If you need to swap more assets, feel free to let me know. + +Error occured. Swap was not performed. Please provide denom for coin: USDC, on Chain Name: axelar. It is necessary as the symbol USDC is not unique among coins on chain axelar. +Select one from found assets: +Symbol: USDC Desc: Circle's stablecoin on Axelar Denom: uusdc, +Symbol: USDC Desc: Circle's stablecoin from Polygon on Axelar Denom: polygon-uusdc, +Symbol: USDC Desc: Circle's stablecoin from Avalanche on Axelar Denom: avalanche-uusdc +``` +5. User response: +``` +Swap 10 OSMO on osmosis to USDC with denom uusdc on axelar +``` +6. Action call and agent response: +``` +Successfuly swapped 10 OSMO tokens to USDC uusdc on chain axelar. +Transaction Hash: E84F36D6EEFAA6D7B70827F34EDAB83258BB86EFE33AEA1F4559D00B30CD3B50 +``` + ## Contribution The plugin includes comprehensive tests. Before submitting any pull requests, ensure all tests pass.