diff --git a/apps/axelar-event-processor/src/approvals-processor/approvals.processor.service.ts b/apps/axelar-event-processor/src/approvals-processor/approvals.processor.service.ts index 0ac921c..9a7cb81 100644 --- a/apps/axelar-event-processor/src/approvals-processor/approvals.processor.service.ts +++ b/apps/axelar-event-processor/src/approvals-processor/approvals.processor.service.ts @@ -19,6 +19,8 @@ import TaskItem = Components.Schemas.TaskItem; import GatewayTransactionTask = Components.Schemas.GatewayTransactionTask; import ExecuteTask = Components.Schemas.ExecuteTask; import RefundTask = Components.Schemas.RefundTask; +import { GasError } from '@mvx-monorepo/common/contracts/entities/gas.error'; +import { GasInfo } from '@mvx-monorepo/common/utils/gas.info'; const MAX_NUMBER_OF_RETRIES = 3; @@ -192,8 +194,18 @@ export class ApprovalsProcessorService { nonce, ); - const gas = await this.transactionsHelper.getTransactionGas(transaction, retry); - transaction.setGasLimit(gas); + try { + const gas = await this.transactionsHelper.getTransactionGas(transaction, retry); + transaction.setGasLimit(gas); + } catch (e) { + // In case the gas estimation fails, the transaction will fail on chain, but we will still send it + // for transparency + if (e instanceof GasError) { + transaction.setGasLimit(GasInfo.GatewayDefault.value); + } else { + throw e; + } + } const txHash = await this.transactionsHelper.signAndSendTransaction(transaction, this.walletSigner); diff --git a/apps/axelar-event-processor/src/approvals-processor/approvals.processor.spec.ts b/apps/axelar-event-processor/src/approvals-processor/approvals.processor.spec.ts index 2c0a75c..9c27f90 100644 --- a/apps/axelar-event-processor/src/approvals-processor/approvals.processor.spec.ts +++ b/apps/axelar-event-processor/src/approvals-processor/approvals.processor.spec.ts @@ -19,6 +19,7 @@ import GatewayTransactionTask = Components.Schemas.GatewayTransactionTask; import TaskItem = Components.Schemas.TaskItem; import RefundTask = Components.Schemas.RefundTask; import ExecuteTask = Components.Schemas.ExecuteTask; +import { GasError } from '@mvx-monorepo/common/contracts/entities/gas.error'; const mockExternalData = BinaryUtils.base64Encode('approveMessages@61726731@61726732'); const mockVerifyData = BinaryUtils.base64Encode('rotateSigners@1234@4321'); @@ -214,6 +215,74 @@ describe('ApprovalsProcessorService', () => { ); }); + + it('Should handle gateway tx task error', async () => { + axelarGmpApi.getTasks.mockReturnValueOnce( + // @ts-ignore + Promise.resolve({ + data: { + tasks: [ + { + type: 'GATEWAY_TX', + task: { + executeData: mockExternalData, + } as GatewayTransactionTask, + id: 'UUID', + timestamp: '1234', + chain: 'multiversx', + }, + ], + }, + }), + ); + + const userAddress = UserAddress.newFromBech32('erd1qqqqqqqqqqqqqpgqhe8t5jewej70zupmh44jurgn29psua5l2jps3ntjj3'); + walletSigner.getAddress.mockReturnValueOnce(userAddress); + + const transaction: DeepMocked = createMock(); + gatewayContract.buildTransactionExternalFunction.mockReturnValueOnce(transaction); + + transactionsHelper.getTransactionGas.mockRejectedValue(new GasError()); + transactionsHelper.signAndSendTransaction.mockReturnValueOnce(Promise.resolve('txHash')); + + await service.handleNewTasksRaw(); + + expect(redisCacheService.get).toHaveBeenCalledTimes(1); + expect(axelarGmpApi.getTasks).toHaveBeenCalledTimes(2); + expect(axelarGmpApi.getTasks).toHaveBeenCalledWith('multiversx', undefined); + expect(axelarGmpApi.getTasks).toHaveBeenCalledWith('multiversx', 'UUID'); + + expect(gatewayContract.buildTransactionExternalFunction).toHaveBeenCalledTimes(1); + expect(gatewayContract.buildTransactionExternalFunction).toHaveBeenCalledWith( + 'approveMessages@61726731@61726732', + userAddress, + 1, + ); + expect(transactionsHelper.getTransactionGas).toHaveBeenCalledTimes(1); + expect(transactionsHelper.getTransactionGas).toHaveBeenCalledWith(transaction, 0); + expect(transaction.setGasLimit).toHaveBeenCalledTimes(1); + expect(transaction.setGasLimit).toHaveBeenCalledWith(50_000_000); + expect(transactionsHelper.signAndSendTransaction).toHaveBeenCalledTimes(1); + expect(transactionsHelper.signAndSendTransaction).toHaveBeenCalledWith(transaction, walletSigner); + + expect(redisCacheService.set).toHaveBeenCalledTimes(2); + expect(redisCacheService.set).toHaveBeenCalledWith( + CacheInfo.PendingTransaction('txHash').key, + { + txHash: 'txHash', + externalData: mockExternalData, + retry: 1, + }, + CacheInfo.PendingTransaction('txHash').ttl, + ); + + expect(redisCacheService.set).toHaveBeenCalledWith( + CacheInfo.LastTaskUUID().key, + 'UUID', + CacheInfo.LastTaskUUID().ttl, + ); + }); + it('Should handle execute task', async () => { axelarGmpApi.getTasks.mockReturnValueOnce( // @ts-ignore diff --git a/libs/common/src/utils/gas.info.ts b/libs/common/src/utils/gas.info.ts index 23ab322..b5c433a 100644 --- a/libs/common/src/utils/gas.info.ts +++ b/libs/common/src/utils/gas.info.ts @@ -16,4 +16,8 @@ export class GasInfo { static Refund: GasInfo = { value: 15_000_000, }; + + static GatewayDefault: GasInfo = { + value: 50_000_000, + }; }