Skip to content

Commit

Permalink
feat: retry on broadcast
Browse files Browse the repository at this point in the history
  • Loading branch information
bangjelkoski committed Jan 3, 2025
1 parent d63f36e commit 6aaed21
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 32 deletions.
2 changes: 1 addition & 1 deletion packages/exceptions/src/exceptions/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ export const chainErrorMessagesMap: Record<
},

'mempool is full': {
message: 'The mempool is full',
message: 'The transaction failed to be included within a block on time.',
code: ChainCosmosErrorCode.ErrMempoolIsFull,
module: TransactionChainErrorModule.CosmosSdk,
},
Expand Down
1 change: 1 addition & 0 deletions packages/exceptions/src/exceptions/utils/maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export const mapFailedTransactionMessage = (

return {
...failedTxMap,
contextModule: failedTxMap.module || contextModule,
message: reason || failedTxMap.message,
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,14 @@ export class IndexerGrpcTransactionApi {
if (e instanceof InjectiveExchangeRpc.GrpcWebError) {
throw new TransactionException(new Error(e.toString()), {
code: e.code,
context: 'PrepareTx',
contextModule: 'Web3Gateway',
type: e.type,
context: 'Web3Gateway.PrepareTx',
})
}

throw new TransactionException(e as Error, {
code: UnspecifiedErrorCode,
context: 'PrepareTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.PrepareTx',
type: ErrorType.Web3Gateway,
})
}
Expand Down Expand Up @@ -179,15 +177,13 @@ export class IndexerGrpcTransactionApi {
throw new TransactionException(new Error(e.toString()), {
code: e.code,
type: e.type,
context: 'CosmosPrepareTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.CosmosPrepareTx',
})
}

throw new TransactionException(e as Error, {
code: UnspecifiedErrorCode,
context: 'CosmosPrepareTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.CosmosPrepareTx',
type: ErrorType.Web3Gateway,
})
}
Expand Down Expand Up @@ -250,15 +246,13 @@ export class IndexerGrpcTransactionApi {
throw new TransactionException(new Error(e.toString()), {
code: e.code,
type: e.type,
context: 'BroadcastTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.BroadcastTx',
})
}

throw new TransactionException(e as Error, {
code: UnspecifiedErrorCode,
context: 'BroadcastTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.BroadcastTx',
type: ErrorType.Web3Gateway,
})
}
Expand Down Expand Up @@ -308,15 +302,13 @@ export class IndexerGrpcTransactionApi {
throw new TransactionException(e.toOriginalError(), {
code: e.code,
type: e.type,
context: 'BroadcastTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.BroadcastTx',
})
}

throw new TransactionException(e as Error, {
code: UnspecifiedErrorCode,
context: 'BroadcastTx',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.BroadcastTx',
type: ErrorType.Web3Gateway,
})
}
Expand All @@ -334,15 +326,13 @@ export class IndexerGrpcTransactionApi {
throw new TransactionException(new Error(e.toString()), {
code: e.code,
type: e.type,
context: 'FeePayer',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.FeePayer',
})
}

throw new TransactionException(e as Error, {
code: UnspecifiedErrorCode,
context: 'FeePayer',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.FeePayer',
type: ErrorType.Web3Gateway,
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,14 @@ export class IndexerGrpcWeb3GwApi extends IndexerGrpcTransactionApi {
if (e instanceof InjectiveExchangeRpc.GrpcWebError) {
throw new TransactionException(new Error(e.toString()), {
code: e.code,
context: 'PrepareEip712',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.PrepareEip712',
type: e.type,
})
}

throw new TransactionException(e as Error, {
code: UnspecifiedErrorCode,
context: 'PrepareEip712',
contextModule: 'Web3Gateway',
context: 'Web3Gateway.PrepareEip712',
type: ErrorType.Web3Gateway,
})
}
Expand Down
58 changes: 54 additions & 4 deletions packages/wallet-ts/src/broadcaster/MsgBroadcaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ import {
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
} from '@injectivelabs/utils'
import {
ChainCosmosErrorCode,
GeneralException,
isThrownException,
ThrownException,
TransactionChainErrorModule,
TransactionException,
UnspecifiedErrorCode,
} from '@injectivelabs/exceptions'
Expand All @@ -56,7 +59,10 @@ import {
isEip712V2OnlyWallet,
} from '../strategies/wallet-strategy/utils.js'
import { Wallet, WalletDeviceType } from '../types/index.js'
import { createEip712StdSignDoc, KeplrWallet } from '../utils/wallets/keplr/index.js'
import {
createEip712StdSignDoc,
KeplrWallet,
} from '../utils/wallets/keplr/index.js'
import { isCosmosAminoOnlyWallet } from '../utils/index.js'
import { LeapWallet } from '../utils/wallets/index.js'
import { checkIfTxRunOutOfGas } from './helper.js'
Expand All @@ -77,6 +83,15 @@ const getEthereumWalletPubKey = <T>({
return hexToBase64(recoverTypedSignaturePubKey(eip712TypedData, signature))
}

const defaultRetriesConfig = () => ({
[`${TransactionChainErrorModule.CosmosSdk}-${ChainCosmosErrorCode.ErrMempoolIsFull}`]:
{
retries: 0,
maxRetries: 10,
timeout: 1000,
},
})

/**
* This class is used to broadcast transactions
* using the WalletStrategy as a handler
Expand All @@ -101,6 +116,8 @@ export class MsgBroadcaster {

public gasBufferCoefficient: number = 1.2

public retriesOnError = defaultRetriesConfig()

constructor(options: MsgBroadcasterOptions) {
const networkInfo = getNetworkInfo(options.network)

Expand Down Expand Up @@ -250,9 +267,11 @@ export class MsgBroadcaster {
* support approach for both cosmos and ethereum native wallets
*
* @param tx
* @returns {string} transaction hash
* @returns {TxResponse}
*/
async broadcastWithFeeDelegation(tx: MsgBroadcasterTxOptions) {
async broadcastWithFeeDelegation(
tx: MsgBroadcasterTxOptions,
): Promise<TxResponse> {
const { options } = this
const { walletStrategy } = options
const txWithAddresses = {
Expand All @@ -279,7 +298,9 @@ export class MsgBroadcaster {
const error = e as any

if (isThrownException(error)) {
throw error
return this.retryOnException(error, () =>
this.broadcastWithFeeDelegation(tx),
)
}

throw new TransactionException(new Error(error))
Expand Down Expand Up @@ -1077,4 +1098,33 @@ export class MsgBroadcaster {

return simulationResponse
}

private async retryOnException<T>(
exception: ThrownException,
retryLogic: () => Promise<T>,
): Promise<any> {
const errorsToRetry = Object.keys(this.retriesOnError)
const errorKey =
`${exception.contextModule}-${exception.contextCode}` as keyof typeof this.retriesOnError

if (!errorsToRetry.includes(errorKey)) {
throw exception
}

const retryConfig = this.retriesOnError[errorKey]

if (retryConfig.retries >= retryConfig.maxRetries) {
this.retriesOnError = defaultRetriesConfig()

throw exception
}

retryConfig.retries += 1

return new Promise((resolve) => {
setTimeout(async () => {
resolve(retryLogic())
}, retryConfig.timeout)
})
}
}
51 changes: 48 additions & 3 deletions packages/wallets/wallet-core/src/broadcaster/MsgBroadcaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ import {
DEFAULT_BLOCK_TIMEOUT_HEIGHT,
} from '@injectivelabs/utils'
import {
ThrownException,
GeneralException,
isThrownException,
TransactionException,
UnspecifiedErrorCode,
ChainCosmosErrorCode,
TransactionException,
TransactionChainErrorModule,
} from '@injectivelabs/exceptions'
import {
getNetworkInfo,
Expand Down Expand Up @@ -76,6 +79,15 @@ const getEthereumWalletPubKey = <T>({
return hexToBase64(recoverTypedSignaturePubKey(eip712TypedData, signature))
}

const defaultRetriesConfig = () => ({
[`${TransactionChainErrorModule.CosmosSdk}-${ChainCosmosErrorCode.ErrMempoolIsFull}`]:
{
retries: 0,
maxRetries: 10,
timeout: 1000,
},
})

/**
* This class is used to broadcast transactions
* using the WalletStrategy as a handler
Expand All @@ -102,6 +114,8 @@ export class MsgBroadcaster {

public gasBufferCoefficient: number = 1.2

public retriesOnError = defaultRetriesConfig()

constructor(options: MsgBroadcasterOptions) {
const networkInfo = getNetworkInfo(options.network)

Expand Down Expand Up @@ -210,7 +224,7 @@ export class MsgBroadcaster {
* @param tx
* @returns {string} transaction hash
*/
async broadcastWithFeeDelegation(tx: MsgBroadcasterTxOptions) {
async broadcastWithFeeDelegation(tx: MsgBroadcasterTxOptions): Promise<TxResponse> {
const { walletStrategy } = this
const txWithAddresses = {
...tx,
Expand All @@ -232,7 +246,9 @@ export class MsgBroadcaster {
const error = e as any

if (isThrownException(error)) {
throw error
return this.retryOnException(error, () =>
this.broadcastWithFeeDelegation(tx),
)
}

throw new TransactionException(new Error(error))
Expand Down Expand Up @@ -1033,4 +1049,33 @@ export class MsgBroadcaster {

return simulationResponse
}

private async retryOnException<T>(
exception: ThrownException,
retryLogic: () => Promise<T>,
): Promise<any> {
const errorsToRetry = Object.keys(this.retriesOnError)
const errorKey =
`${exception.contextModule}-${exception.contextCode}` as keyof typeof this.retriesOnError

if (!errorsToRetry.includes(errorKey)) {
throw exception
}

const retryConfig = this.retriesOnError[errorKey]

if (retryConfig.retries >= retryConfig.maxRetries) {
this.retriesOnError = defaultRetriesConfig()

throw exception
}

retryConfig.retries += 1

return new Promise((resolve) => {
setTimeout(async () => {
resolve(retryLogic())
}, retryConfig.timeout)
})
}
}

0 comments on commit 6aaed21

Please sign in to comment.