From ff74a4eaef0a507894bbdef3c0b2de16e63eb7df Mon Sep 17 00:00:00 2001 From: Jon Ator Date: Tue, 6 Aug 2024 16:38:19 -0400 Subject: [PATCH] fix getting timeout height for intermediate IBC transfer --- packages/bridge/src/axelar/index.ts | 19 +++++++-------- packages/bridge/src/interface.ts | 8 +++++-- .../__tests__/skip-bridge-provider.spec.ts | 6 ++++- packages/bridge/src/skip/index.ts | 19 +++++++++------ packages/bridge/src/squid/index.ts | 24 ++++++++++--------- .../src/queries/complex/get-timeout-height.ts | 9 ++++++- 6 files changed, 52 insertions(+), 33 deletions(-) diff --git a/packages/bridge/src/axelar/index.ts b/packages/bridge/src/axelar/index.ts index 967b2e3299..59c77e4bf1 100644 --- a/packages/bridge/src/axelar/index.ts +++ b/packages/bridge/src/axelar/index.ts @@ -51,6 +51,7 @@ export class AxelarBridgeProvider implements BridgeProvider { protected _queryClient: AxelarQueryAPI | null = null; protected _assetTransferClient: AxelarAssetTransfer | null = null; protected protoRegistry = new Registry(ibcProtoRegistry); + protected axelarChainId: string; protected readonly axelarScanBaseUrl: string; protected readonly axelarApiBaseUrl: string; @@ -64,6 +65,8 @@ export class AxelarBridgeProvider implements BridgeProvider { this.ctx.env === "mainnet" ? "https://api.axelarscan.io" : "https://testnet.api.axelarscan.io"; + this.axelarChainId = + ctx.env === "mainnet" ? "axelar-dojo-1" : "axelar-testnet-lisbon-3"; } async getQuote(params: GetBridgeQuoteParams): Promise { @@ -568,7 +571,7 @@ export class AxelarBridgeProvider implements BridgeProvider { }); const timeoutHeight = await this.ctx.getTimeoutHeight({ - chainId: toChain.chainId.toString(), + chainId: this.axelarChainId, }); const ibcAsset = getAssetFromAssetList({ @@ -578,11 +581,7 @@ export class AxelarBridgeProvider implements BridgeProvider { }); if (!ibcAsset) { - throw new BridgeQuoteError({ - bridgeId: AxelarBridgeProvider.ID, - errorType: "CreateCosmosTxError", - message: "Could not find IBC asset info", - }); + throw new Error("Could not find IBC asset info: " + fromAsset.denom); } const ibcTransferMethod = ibcAsset.rawAsset.transferMethods.find( @@ -590,11 +589,9 @@ export class AxelarBridgeProvider implements BridgeProvider { ) as IbcTransferMethod | undefined; if (!ibcTransferMethod) { - throw new BridgeQuoteError({ - bridgeId: AxelarBridgeProvider.ID, - errorType: "CreateCosmosTxError", - message: "Could not find IBC asset transfer info", - }); + throw new Error( + "Could not find IBC asset transfer info: " + ibcAsset.symbol + ); } const { typeUrl, value: msg } = cosmosMsgOpts.ibcTransfer.messageComposer( diff --git a/packages/bridge/src/interface.ts b/packages/bridge/src/interface.ts index 9b0d898922..f58f539dd1 100644 --- a/packages/bridge/src/interface.ts +++ b/packages/bridge/src/interface.ts @@ -12,8 +12,12 @@ export interface BridgeProviderContext { assetLists: AssetList[]; chainList: Chain[]; - /** Provides current timeout height for a chain of given chainId. */ - getTimeoutHeight(params: { chainId: string }): Promise<{ + /** Provides current timeout height for a chain of given chainId. + * If a destination address is provided, the bech32Prefix will be used to get the chain. */ + getTimeoutHeight(params: { + chainId?: string; + destinationAddress?: string; + }): Promise<{ revisionNumber: string | undefined; revisionHeight: string; }>; diff --git a/packages/bridge/src/skip/__tests__/skip-bridge-provider.spec.ts b/packages/bridge/src/skip/__tests__/skip-bridge-provider.spec.ts index 56c03585b4..7f3a05b315 100644 --- a/packages/bridge/src/skip/__tests__/skip-bridge-provider.spec.ts +++ b/packages/bridge/src/skip/__tests__/skip-bridge-provider.spec.ts @@ -339,7 +339,11 @@ describe("SkipBridgeProvider", () => { const txRequest = (await provider.createTransaction( "1", - "osmosis-1", + { + chainId: "osmosis-1", + chainName: "osmosis", + chainType: "cosmos", + }, "0xabc", messages )) as EvmBridgeTransactionRequest; diff --git a/packages/bridge/src/skip/index.ts b/packages/bridge/src/skip/index.ts index edd704b66d..a29b81303f 100644 --- a/packages/bridge/src/skip/index.ts +++ b/packages/bridge/src/skip/index.ts @@ -215,7 +215,7 @@ export class SkipBridgeProvider implements BridgeProvider { const transactionRequest = await this.createTransaction( fromChain.chainId.toString(), - toChain.chainId.toString(), + toChain, fromAddress as Address, msgs ); @@ -444,7 +444,7 @@ export class SkipBridgeProvider implements BridgeProvider { async createTransaction( fromChainId: string, - toChainId: string, + toChain: BridgeChain, address: Address, messages: SkipMsg[] ) { @@ -459,7 +459,7 @@ export class SkipBridgeProvider implements BridgeProvider { if ("multi_chain_msg" in message) { return await this.createCosmosTransaction( - toChainId, + toChain, message.multi_chain_msg ); } @@ -467,7 +467,7 @@ export class SkipBridgeProvider implements BridgeProvider { } async createCosmosTransaction( - toChainId: string, + toChain: BridgeChain, message: SkipMultiChainMsg ): Promise { const messageData = JSON.parse(message.msg); @@ -502,9 +502,14 @@ export class SkipBridgeProvider implements BridgeProvider { } else { // is an ibc transfer - const timeoutHeight = await this.ctx.getTimeoutHeight({ - chainId: toChainId, - }); + // If toChain is not cosmos, this IBC transfer is an + // intermediary IBC transfer where we need to get the + // timeout from the bech32 prefix of the receiving address + const timeoutHeight = await this.ctx.getTimeoutHeight( + toChain.chainType === "cosmos" + ? toChain + : { destinationAddress: messageData.receiver } + ); const { typeUrl, value } = cosmosMsgOpts.ibcTransfer.messageComposer({ sourcePort: messageData.source_port, diff --git a/packages/bridge/src/squid/index.ts b/packages/bridge/src/squid/index.ts index e473f1537f..3245a3f097 100644 --- a/packages/bridge/src/squid/index.ts +++ b/packages/bridge/src/squid/index.ts @@ -271,7 +271,7 @@ export class SquidBridgeProvider implements BridgeProvider { : await this.createCosmosTransaction( transactionRequest.data, fromAddress, - toChain.chainId.toString(), + toChain, { denom: fromAsset.address, amount: fromAmount } // TODO: uncomment when we're able to find a way to get gas limit from Squid // or get it ourselves @@ -495,7 +495,7 @@ export class SquidBridgeProvider implements BridgeProvider { async createCosmosTransaction( data: string, fromAddress: string, - toChainId: string, + toChain: BridgeChain, fromCoin: { denom: string; amount: string; @@ -533,9 +533,14 @@ export class SquidBridgeProvider implements BridgeProvider { }; }; - const timeoutHeight = await this.ctx.getTimeoutHeight({ - chainId: toChainId, - }); + // If toChain is not cosmos, this IBC transfer is an + // intermediary IBC transfer where we need to get the + // timeout from the bech32 prefix of the receiving address + const timeoutHeight = await this.ctx.getTimeoutHeight( + toChain.chainType === "cosmos" + ? toChain + : { destinationAddress: ibcData.msg.receiver } + ); const { typeUrl, value: msg } = cosmosMsgOpts.ibcTransfer.messageComposer({ @@ -587,12 +592,9 @@ export class SquidBridgeProvider implements BridgeProvider { }; } - throw new BridgeQuoteError({ - bridgeId: SquidBridgeProvider.ID, - errorType: "CreateCosmosTxError", - message: - "Unknown message type. Osmosis FrontEnd only supports the IBC transfer and cosmwasm executeMsg message type", - }); + throw new Error( + "Unknown message type. Osmosis FrontEnd only supports the IBC transfer and cosmwasm executeMsg message type" + ); } catch (e) { const error = e as Error | BridgeQuoteError; diff --git a/packages/server/src/queries/complex/get-timeout-height.ts b/packages/server/src/queries/complex/get-timeout-height.ts index bee9cf0ef6..7309c22a9e 100644 --- a/packages/server/src/queries/complex/get-timeout-height.ts +++ b/packages/server/src/queries/complex/get-timeout-height.ts @@ -7,13 +7,20 @@ import { queryRPCStatus } from "../../queries/cosmos"; export async function getTimeoutHeight({ chainList, chainId, + destinationAddress, }: { chainList: Chain[]; - chainId: string; + chainId?: string; + /** + * WARNING: bech32 prefix may be the same across different chains, + * retulting in the use of an unintended chain. + */ + destinationAddress?: string; }) { const destinationCosmosChain = getChain({ chainList, chainId, + destinationAddress, }); if (!destinationCosmosChain) {