diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts index 88e92015622..f197fa1646c 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/index.ts @@ -123,7 +123,7 @@ export const createIBCTransferAction = ( const availableChains = runtime.getSetting("COSMOS_AVAILABLE_CHAINS"); const availableChainsArray = availableChains?.split(","); - return !(mnemonic && availableChains && availableChainsArray.length); + return !!(mnemonic && availableChains && availableChainsArray.length); }, examples: [ [ diff --git a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts index 62e30be681d..a288031ccf3 100644 --- a/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts +++ b/packages/plugin-cosmos/src/actions/ibc-transfer/schema.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const IBCTransferParamsSchema = z.object({ chainName: z.string(), symbol: z.string(), - amount: z.string(), - toAddress: z.string(), + amount: z.string().regex(/^\d+$/, "Amount must be a numeric string"), + toAddress: z.string().regex(/^[a-z0-9]+$/, "Invalid bech32 address format"), targetChainName: z.string(), }); 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 3fcfdab3705..adfa46abb87 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 @@ -13,10 +13,20 @@ export const bridgeDenomProvider: IDenomProvider = async ( sourceAssetChainId ); - const ibcAssetData = bridgeData.dest_assets[destChainId]?.assets?.find( + const destAssets = bridgeData.dest_assets[destChainId]; + + if (!destAssets?.assets) { + throw new Error(`No assets found for chain ${destChainId}`); + } + + const ibcAssetData = destAssets.assets?.find( ({ origin_denom }) => origin_denom === sourceAssetDenom ); + if (!ibcAssetData) { + throw new Error(`No matching asset found for denom ${sourceAssetDenom}`); + } + if (!ibcAssetData.denom) { throw new Error("No IBC asset data"); } 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 508b1cc29a9..90b94536d66 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 @@ -77,12 +77,18 @@ export class IBCTransferAction implements ICosmosActionService { throw new Error("Cannot find destination chain"); } - const { denom: destAssetDenom } = await bridgeDenomProvider( + const bridgeDenomResult = await bridgeDenomProvider( denom.base, sourceChain.chain_id, destChain.chain_id ); + if (!bridgeDenomResult || !bridgeDenomResult.denom) { + throw new Error("Failed to get destination asset denomination"); + } + + const destAssetDenom = bridgeDenomResult.denom; + const route = await skipClient.route({ destAssetChainID: destChain.chain_id, destAssetDenom, @@ -110,13 +116,22 @@ export class IBCTransferAction implements ICosmosActionService { let txHash: string | undefined; - await skipClient.executeRoute({ - route, - userAddresses, - onTransactionCompleted: async (_, executeRouteTxHash) => { - txHash = executeRouteTxHash; - }, - }); + try { + await skipClient.executeRoute({ + route, + userAddresses, + onTransactionCompleted: async (_, executeRouteTxHash) => { + txHash = executeRouteTxHash; + }, + }); + } catch (error) { + throw new Error(`Failed to execute route: ${error?.message}`); + } + + if (!txHash) { + throw new Error("Transaction hash is undefined after executing route"); + } + return { from: senderAddress, to: params.toAddress, diff --git a/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts index caad69d4b3b..1f0a0504d18 100644 --- a/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts +++ b/packages/plugin-cosmos/src/shared/services/skip-api/assets-from-source-fetcher/skip-api-assets-from-source-fetcher.ts @@ -58,6 +58,7 @@ export class SkipApiAssetsFromSourceFetcher { headers: { "Content-Type": "application/json", }, + timeout: 5000, }); const validResponse = skipApiAssetsFromSourceResponseSchema.parse( 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..e178992d43b 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 @@ -140,6 +140,7 @@ describe("IBCTransferAction", () => { const senderAddress = "cosmos1senderaddress"; const targetChainId = "target-chain-id"; const sourceChainId = "source-chain-id"; + const mockTxHash = "mock_tx_hash_123"; mockWalletChains.getWalletAddress.mockResolvedValue(senderAddress); // @ts-expect-error --- ... @@ -149,18 +150,15 @@ describe("IBCTransferAction", () => { getAssetBySymbol.mockReturnValue({ base: "uatom", }); - const params = { - chainName: "test-chain", - targetChainName: "target-chain", - symbol: "ATOM", - amount: "10", - toAddress: "cosmos1receiveraddress", - }; mockBridgeDenomProvider.mockResolvedValue({ denom: "uatom" }); mockSkipClient.route.mockResolvedValue({ requiredChainAddresses: [sourceChainId, targetChainId], }); + mockSkipClient.executeRoute.mockImplementation(async ({ onTransactionCompleted }) => { + await onTransactionCompleted(null, mockTxHash); + }); + // @ts-expect-error --- ... const ibcTransferAction = new IBCTransferAction(mockWalletChains); @@ -173,7 +171,7 @@ describe("IBCTransferAction", () => { expect(result).toEqual({ from: senderAddress, to: params.toAddress, - txHash: undefined, + txHash: mockTxHash, }); expect(mockSkipClient.executeRoute).toHaveBeenCalled(); });