From d548126aec2724b4fefa9ffab0608be774dd0e23 Mon Sep 17 00:00:00 2001 From: 0xBossanova <0xBossanova@proton.me> Date: Wed, 5 Apr 2023 15:18:42 +0200 Subject: [PATCH 1/2] bug: fix sequence error and cleanup nomempoolloop --- .../chainOperator/chainAdapters/cosmjs.ts | 114 +++++++++----- .../chainOperator/chainAdapters/injective.ts | 143 +++++++++++------- .../chainOperator/chainOperatorInterface.ts | 1 + src/core/chainOperator/chainoperator.ts | 7 + src/core/types/arbitrageloops/mempoolLoop.ts | 10 +- .../types/arbitrageloops/nomempoolLoop.ts | 43 +++--- src/core/types/arbitrageloops/skipLoop.ts | 2 +- src/index.ts | 2 +- 8 files changed, 200 insertions(+), 122 deletions(-) diff --git a/src/core/chainOperator/chainAdapters/cosmjs.ts b/src/core/chainOperator/chainAdapters/cosmjs.ts index 236cc87..c1b8369 100644 --- a/src/core/chainOperator/chainAdapters/cosmjs.ts +++ b/src/core/chainOperator/chainAdapters/cosmjs.ts @@ -16,27 +16,53 @@ import { ChainOperatorInterface, TxResponse } from "../chainOperatorInterface"; * */ class CosmjsAdapter implements ChainOperatorInterface { - signingCWClient!: SigningCosmWasmClient; //used to sign transactions - tmClient!: Tendermint34Client; //used to broadcast transactions - httpClient: HttpBatchClient | HttpClient; //used to query rpc methods (unconfirmed_txs, account) - wasmQueryClient!: QueryClient & WasmExtension; //used to query wasm methods (contract states) - account!: AccountData; - publicAddress!: string; - accountNumber = 0; - sequence = 0; - chainId!: string; - signer!: DirectSecp256k1HdWallet; - skipBundleClient?: SkipBundleClient; + private _signingCWClient!: SigningCosmWasmClient; //used to sign transactions + private _tmClient!: Tendermint34Client; //used to broadcast transactions + private _httpClient: HttpBatchClient | HttpClient; //used to query rpc methods (unconfirmed_txs, account) + private _wasmQueryClient!: QueryClient & WasmExtension; //used to query wasm methods (contract states) + private _account!: AccountData; + private _publicAddress!: string; + private _accountNumber = 0; + private _sequence = 0; + + private _chainId!: string; + + private _signer!: DirectSecp256k1HdWallet; + private _skipBundleClient?: SkipBundleClient; /** * */ constructor(botConfig: BotConfig) { - this.httpClient = new HttpBatchClient(botConfig.rpcUrl); + this._httpClient = new HttpBatchClient(botConfig.rpcUrl); if (botConfig.skipConfig) { - this.skipBundleClient = new SkipBundleClient(botConfig.skipConfig.skipRpcUrl); + this._skipBundleClient = new SkipBundleClient(botConfig.skipConfig.skipRpcUrl); } } + /** + * + */ + public get sequence() { + return this._sequence; + } + /** + * + */ + public set sequence(value) { + this._sequence = value; + } + /** + * + */ + public get publicAddress(): string { + return this._publicAddress; + } + /** + * + */ + public get chainId(): string { + return this._chainId; + } /** * */ @@ -45,28 +71,27 @@ class CosmjsAdapter implements ChainOperatorInterface { const signer = await DirectSecp256k1HdWallet.fromMnemonic(botConfig.mnemonic, { prefix: botConfig.chainPrefix, }); - this.signer = signer; + this._signer = signer; // connect to client and querier - this.signingCWClient = await SigningCosmWasmClient.connectWithSigner(botConfig.rpcUrl, signer, { + this._signingCWClient = await SigningCosmWasmClient.connectWithSigner(botConfig.rpcUrl, signer, { prefix: botConfig.chainPrefix, gasPrice: GasPrice.fromString(botConfig.gasPrice + botConfig.baseDenom), }); - this.httpClient = new HttpBatchClient(botConfig.rpcUrl); - this.tmClient = await Tendermint34Client.create(this.httpClient); - this.wasmQueryClient = QueryClient.withExtensions(this.tmClient, setupWasmExtension, setupAuthExtension); - this.account = (await signer.getAccounts())[0]; - const { accountNumber, sequence } = await this.signingCWClient.getSequence(this.account.address); - this.chainId = await this.signingCWClient.getChainId(); - this.accountNumber = accountNumber; - this.sequence = sequence; - this.publicAddress = this.account.address; + this._tmClient = await Tendermint34Client.create(this._httpClient); + this._wasmQueryClient = QueryClient.withExtensions(this._tmClient, setupWasmExtension, setupAuthExtension); + this._account = (await signer.getAccounts())[0]; + const { accountNumber, sequence } = await this._signingCWClient.getSequence(this._account.address); + this._chainId = await this._signingCWClient.getChainId(); + this._accountNumber = accountNumber; + this._sequence = sequence; + this._publicAddress = this._account.address; } /** * */ async queryContractSmart(address: string, queryMsg: Record): Promise { - return await this.wasmQueryClient.wasm.queryContractSmart(address, queryMsg); + return await this._wasmQueryClient.wasm.queryContractSmart(address, queryMsg); } /** * @@ -77,16 +102,16 @@ class CosmjsAdapter implements ChainOperatorInterface { memo?: string | undefined, ): Promise { if (fee === "auto") { - return await this.signingCWClient.signAndBroadcast(this.publicAddress, msgs, fee, memo); + return await this._signingCWClient.signAndBroadcast(this.publicAddress, msgs, fee, memo); } else { const signerData = { - accountNumber: this.accountNumber, - sequence: this.sequence, - chainId: this.chainId, + accountNumber: this._accountNumber, + sequence: this._sequence, + chainId: this._chainId, }; - const txRaw = await this.signingCWClient.sign(this.publicAddress, msgs, fee, "memo", signerData); + const txRaw = await this._signingCWClient.sign(this.publicAddress, msgs, fee, "memo", signerData); const txBytes = TxRaw.encode(txRaw).finish(); - const res = await this.tmClient.broadcastTxSync({ tx: txBytes }); + const res = await this._tmClient.broadcastTxSync({ tx: txBytes }); console.log(res); return { height: 0, @@ -100,25 +125,25 @@ class CosmjsAdapter implements ChainOperatorInterface { * */ async signAndBroadcastSkipBundle(messages: Array, fee: StdFee, memo?: string, otherTx?: TxRaw) { - if (!this.skipBundleClient) { + if (!this._skipBundleClient) { console.log("skip bundle client not initialised"); process.exit(1); } const signerData = { - accountNumber: this.accountNumber, - sequence: this.sequence, - chainId: this.chainId, + accountNumber: this._accountNumber, + sequence: this._sequence, + chainId: this._chainId, }; - const txRaw: TxRaw = await this.signingCWClient.sign(this.publicAddress, messages, fee, "", signerData); + const txRaw: TxRaw = await this._signingCWClient.sign(this.publicAddress, messages, fee, "", signerData); let signed; if (otherTx) { - signed = await this.skipBundleClient.signBundle([otherTx, txRaw], this.signer, this.publicAddress); + signed = await this._skipBundleClient.signBundle([otherTx, txRaw], this._signer, this.publicAddress); } else { - signed = await this.skipBundleClient.signBundle([txRaw], this.signer, this.publicAddress); + signed = await this._skipBundleClient.signBundle([txRaw], this._signer, this.publicAddress); } - const res = await this.skipBundleClient.sendBundle(signed, 0, true); + const res = await this._skipBundleClient.sendBundle(signed, 0, true); return res; } @@ -126,9 +151,18 @@ class CosmjsAdapter implements ChainOperatorInterface { * */ async queryMempool(): Promise { - const mempoolResult = await this.httpClient.execute(createJsonRpcRequest("unconfirmed_txs")); + const mempoolResult = await this._httpClient.execute(createJsonRpcRequest("unconfirmed_txs")); return mempoolResult.result; } + + /** + * + */ + async reset(): Promise { + const { accountNumber, sequence } = await this._signingCWClient.getSequence(this._account.address); + this._accountNumber = accountNumber; + this._sequence = sequence; + } } export default CosmjsAdapter; diff --git a/src/core/chainOperator/chainAdapters/injective.ts b/src/core/chainOperator/chainAdapters/injective.ts index c1bf93f..37841d9 100644 --- a/src/core/chainOperator/chainAdapters/injective.ts +++ b/src/core/chainOperator/chainAdapters/injective.ts @@ -12,7 +12,6 @@ import { ChainGrpcWasmApi, ChainRestAuthApi, createTransaction, - getDefaultSubaccountId, IndexerGrpcSpotApi, MsgBroadcasterWithPk, MsgExecuteContract, @@ -34,22 +33,22 @@ import { ChainOperatorInterface, TxResponse } from "../chainOperatorInterface"; * */ class InjectiveAdapter implements ChainOperatorInterface { - privateKey: PrivateKey; - signAndBroadcastClient: MsgBroadcasterWithPk; - spotQueryClient: IndexerGrpcSpotApi; - wasmQueryClient: ChainGrpcWasmApi; - httpClient: HttpBatchClient; - chainId: ChainId; - network: Network; - publicKey: PublicKey; - publicAddress: string; - ethereumAddress: string; - subAccountId: string; - signer!: DirectSecp256k1HdWallet; - accountNumber = 0; - sequence = 0; - skipBundleClient?: SkipBundleClient; - skipSigningAddress!: string; + private _privateKey: PrivateKey; + private _signAndBroadcastClient: MsgBroadcasterWithPk; + private _spotQueryClient: IndexerGrpcSpotApi; + private _wasmQueryClient: ChainGrpcWasmApi; + private _httpClient: HttpBatchClient; + private _chainId: ChainId; + + private _network: Network; + private _publicKey: PublicKey; + private _publicAddress: string; + + private _signer!: DirectSecp256k1HdWallet; + private _accountNumber = 0; + private _sequence = 0; + private _skipBundleClient?: SkipBundleClient; + private _skipSigningAddress!: string; /** * @@ -57,49 +56,72 @@ class InjectiveAdapter implements ChainOperatorInterface { constructor(botConfig: BotConfig, network: Network = Network.MainnetK8s) { const endpoints = getNetworkEndpoints(network); const privateKey = PrivateKey.fromMnemonic(botConfig.mnemonic, "m/44'/60'/0'/0/0"); - this.privateKey = privateKey; - this.signAndBroadcastClient = new MsgBroadcasterWithPk({ + this._privateKey = privateKey; + this._signAndBroadcastClient = new MsgBroadcasterWithPk({ network: network, privateKey: privateKey, }); - this.spotQueryClient = new IndexerGrpcSpotApi(endpoints.indexer); - this.wasmQueryClient = new ChainGrpcWasmApi(endpoints.grpc); - this.httpClient = new HttpBatchClient(botConfig.rpcUrl); - this.chainId = network === Network.TestnetK8s ? ChainId.Testnet : ChainId.Mainnet; - this.network = network; - this.publicKey = privateKey.toPublicKey(); - this.publicAddress = privateKey.toPublicKey().toAddress().address; - this.subAccountId = getDefaultSubaccountId(this.publicAddress); - this.ethereumAddress = privateKey.toPublicKey().toAddress().getEthereumAddress(); + this._spotQueryClient = new IndexerGrpcSpotApi(endpoints.indexer); + this._wasmQueryClient = new ChainGrpcWasmApi(endpoints.grpc); + this._httpClient = new HttpBatchClient(botConfig.rpcUrl); + this._chainId = network === Network.TestnetK8s ? ChainId.Testnet : ChainId.Mainnet; + this._network = network; + this._publicKey = privateKey.toPublicKey(); + this._publicAddress = privateKey.toPublicKey().toAddress().address; + } + /** + * + */ + public get sequence() { + return this._sequence; + } + /** + * + */ + public set sequence(value) { + this._sequence = value; + } + /** + * + */ + public get publicAddress(): string { + return this._publicAddress; } + /** + * + */ + public get chainId(): ChainId { + return this._chainId; + } + /** * */ async init(botConfig: BotConfig): Promise { - const restEndpoint = getNetworkEndpoints(this.network).rest; + const restEndpoint = getNetworkEndpoints(this._network).rest; const chainRestAuthApi = new ChainRestAuthApi(restEndpoint); - const accountDetailsResponse = await chainRestAuthApi.fetchAccount(this.publicAddress); + const accountDetailsResponse = await chainRestAuthApi.fetchAccount(this._publicAddress); const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse); const accountDetails = baseAccount.toAccountDetails(); - this.accountNumber = accountDetails.accountNumber; - this.sequence = accountDetails.sequence; + this._accountNumber = accountDetails.accountNumber; + this._sequence = accountDetails.sequence; const hdPath = stringToPath("m/44'/60'/0'/0/0"); - this.signer = await DirectSecp256k1HdWallet.fromMnemonic(botConfig.mnemonic, { + this._signer = await DirectSecp256k1HdWallet.fromMnemonic(botConfig.mnemonic, { prefix: botConfig.chainPrefix, hdPaths: [hdPath], }); if (botConfig.skipConfig) { - this.skipBundleClient = new SkipBundleClient(botConfig.skipConfig.skipRpcUrl); - this.skipSigningAddress = (await this.signer.getAccounts())[0].address; + this._skipBundleClient = new SkipBundleClient(botConfig.skipConfig.skipRpcUrl); + this._skipSigningAddress = (await this._signer.getAccounts())[0].address; } } /** * */ async queryContractSmart(address: string, queryMsg: Record): Promise { - const queryResult = await this.wasmQueryClient.fetchSmartContractState( + const queryResult = await this._wasmQueryClient.fetchSmartContractState( address, Buffer.from(JSON.stringify(queryMsg)).toString("base64"), ); @@ -110,9 +132,22 @@ class InjectiveAdapter implements ChainOperatorInterface { * */ async queryMempool(): Promise { - const mempoolResult = await this.httpClient.execute(createJsonRpcRequest("unconfirmed_txs")); + const mempoolResult = await this._httpClient.execute(createJsonRpcRequest("unconfirmed_txs")); return mempoolResult.result; } + + /** + * + */ + async reset(): Promise { + const restEndpoint = getNetworkEndpoints(this._network).rest; + const chainRestAuthApi = new ChainRestAuthApi(restEndpoint); + const accountDetailsResponse = await chainRestAuthApi.fetchAccount(this._publicAddress); + const baseAccount = BaseAccount.fromRestApi(accountDetailsResponse); + const accountDetails = baseAccount.toAccountDetails(); + this._accountNumber = accountDetails.accountNumber; + this._sequence = accountDetails.sequence; + } /** * */ @@ -126,11 +161,11 @@ class InjectiveAdapter implements ChainOperatorInterface { if (!fee || fee === "auto") { const broadcasterOptions = { msgs: preppedMsgs, - injectiveAddress: this.publicAddress, + injectiveAddress: this._publicAddress, }; - const simRes = await this.signAndBroadcastClient.simulate(broadcasterOptions); + const simRes = await this._signAndBroadcastClient.simulate(broadcasterOptions); console.log("simulation succesful: \n", simRes); - const res = await this.signAndBroadcastClient.broadcast(broadcasterOptions); + const res = await this._signAndBroadcastClient.broadcast(broadcasterOptions); return { height: res.height, code: res.code, @@ -140,10 +175,10 @@ class InjectiveAdapter implements ChainOperatorInterface { } else { const broadcasterOptions = { msgs: preppedMsgs, - injectiveAddress: this.publicAddress, + injectiveAddress: this._publicAddress, gasLimit: +fee.gas, }; - const res = await this.signAndBroadcastClient.broadcast(broadcasterOptions); + const res = await this._signAndBroadcastClient.broadcast(broadcasterOptions); return { height: res.height, code: res.code, @@ -168,7 +203,7 @@ class InjectiveAdapter implements ChainOperatorInterface { * */ async signAndBroadcastSkipBundle(messages: Array, fee: StdFee, memo?: string, otherTx?: TxRaw) { - if (!this.skipBundleClient || !this.skipSigningAddress) { + if (!this._skipBundleClient || !this._skipSigningAddress) { console.log("skip bundle client not initialised"); process.exit(1); } @@ -181,13 +216,13 @@ class InjectiveAdapter implements ChainOperatorInterface { const { signBytes, txRaw, bodyBytes, authInfoBytes } = createTransaction({ fee: fee, memo: memo, - chainId: this.chainId, + chainId: this._chainId, message: preppedMsgs.map((msg) => msg.toDirectSign()), - pubKey: this.publicKey.toBase64(), - sequence: this.sequence, - accountNumber: this.accountNumber, + pubKey: this._publicKey.toBase64(), + sequence: this._sequence, + accountNumber: this._accountNumber, }); - const signature = await this.privateKey.sign(Buffer.from(signBytes)); + const signature = await this._privateKey.sign(Buffer.from(signBytes)); txRaw.setSignaturesList([signature]); const cosmTxRaw = { @@ -198,12 +233,16 @@ class InjectiveAdapter implements ChainOperatorInterface { let signed; if (otherTx) { - signed = await this.skipBundleClient.signBundle([otherTx, cosmTxRaw], this.signer, this.skipSigningAddress); + signed = await this._skipBundleClient.signBundle( + [otherTx, cosmTxRaw], + this._signer, + this._skipSigningAddress, + ); } else { - signed = await this.skipBundleClient.signBundle([cosmTxRaw], this.signer, this.skipSigningAddress); + signed = await this._skipBundleClient.signBundle([cosmTxRaw], this._signer, this._skipSigningAddress); } console.log(inspect(signed, { depth: null })); - const res = await this.skipBundleClient.sendBundle(signed, 0, true); + const res = await this._skipBundleClient.sendBundle(signed, 0, true); return res; } /** @@ -234,7 +273,7 @@ class InjectiveAdapter implements ChainOperatorInterface { // Provide LP: Funds isint being handled proper, before we were sending 1 coin, now we are sending it all but getting invalid coins const params = { funds: isLPMessage ? funds : funds?.[0], - sender: this.publicAddress, + sender: this._publicAddress, contractAddress: contract, exec: executeMessageJson, diff --git a/src/core/chainOperator/chainOperatorInterface.ts b/src/core/chainOperator/chainOperatorInterface.ts index 05ab1f7..e5816d9 100644 --- a/src/core/chainOperator/chainOperatorInterface.ts +++ b/src/core/chainOperator/chainOperatorInterface.ts @@ -24,6 +24,7 @@ export interface ChainOperatorInterface { // getNetwork: () => Promise; // getBalance: (address: string, searchDenom: string) => Promise; queryMempool: () => Promise; + reset: () => Promise; } export interface TxResponse { diff --git a/src/core/chainOperator/chainoperator.ts b/src/core/chainOperator/chainoperator.ts index a878338..b7a9100 100644 --- a/src/core/chainOperator/chainoperator.ts +++ b/src/core/chainOperator/chainoperator.ts @@ -63,6 +63,13 @@ export class ChainOperator { ): Promise { return await this.client.signAndBroadcast(msgs, fee, memo); } + + /** + * + */ + async reset() { + return await this.client.reset(); + } /** * */ diff --git a/src/core/types/arbitrageloops/mempoolLoop.ts b/src/core/types/arbitrageloops/mempoolLoop.ts index 2aff01e..e27cb2e 100644 --- a/src/core/types/arbitrageloops/mempoolLoop.ts +++ b/src/core/types/arbitrageloops/mempoolLoop.ts @@ -109,15 +109,17 @@ export class MempoolLoop { break; } } + return; } /** * */ - public reset() { + async reset() { this.unCDPaths(); this.totalBytes = 0; flushTxMemory(); + await this.chainOperator.reset(); } /** @@ -139,9 +141,11 @@ export class MempoolLoop { const txResponse = await this.chainOperator.signAndBroadcast(msgs, TX_FEE); await this.logger?.sendMessage(JSON.stringify(txResponse), LogType.Console); - this.chainOperator.client.sequence += 1; + + if (txResponse.code === 0) { + this.chainOperator.client.sequence = this.chainOperator.client.sequence + 1; + } await delay(5000); - // await this.fetchRequiredChainData(); } /** diff --git a/src/core/types/arbitrageloops/nomempoolLoop.ts b/src/core/types/arbitrageloops/nomempoolLoop.ts index e2d0571..c3e57bf 100644 --- a/src/core/types/arbitrageloops/nomempoolLoop.ts +++ b/src/core/types/arbitrageloops/nomempoolLoop.ts @@ -5,7 +5,7 @@ import { OptimalTrade } from "../../arbitrage/arbitrage"; import { ChainOperator } from "../../chainOperator/chainoperator"; import { Logger } from "../../logging"; import { BotConfig } from "../base/botConfig"; -import { flushTxMemory, Mempool } from "../base/mempool"; +import { Mempool } from "../base/mempool"; import { Path } from "../base/path"; import { Pool } from "../base/pool"; @@ -70,43 +70,36 @@ export class NoMempoolLoop { this.pathlib = pathlib; } - /** - * - */ - public async fetchRequiredChainData() { - // const { accountNumber, sequence } = await this.botClients.SigningCWClient.getSequence(this.account.address); - // this.sequence = sequence; - // this.accountNumber = accountNumber; - // const chainId = await this.botClients.SigningCWClient.getChainId(); - // this.chainid = chainId; - } - /** * */ public async step() { - this.iterations++; - await this.updateStateFunction(this.chainOperator, this.pools); + while (true) { + this.iterations++; + await this.updateStateFunction(this.chainOperator, this.pools); + + const arbTrade: OptimalTrade | undefined = this.arbitrageFunction(this.paths, this.botConfig); - const arbTrade: OptimalTrade | undefined = this.arbitrageFunction(this.paths, this.botConfig); + if (arbTrade) { + console.log(inspect(arbTrade.path.pools, { showHidden: true, depth: 4, colors: true })); + console.log(inspect(arbTrade.offerAsset, { showHidden: true, depth: 3, colors: true })); + console.log("expected profit: ", arbTrade.profit); + await this.trade(arbTrade); + this.cdPaths(arbTrade.path); + break; + } - if (arbTrade) { - console.log(inspect(arbTrade.path.pools, { showHidden: true, depth: 4, colors: true })); - console.log(inspect(arbTrade.offerAsset, { showHidden: true, depth: 3, colors: true })); - console.log("expected profit: ", arbTrade.profit); - await this.trade(arbTrade); - this.cdPaths(arbTrade.path); - return; + await delay(1500); } + return; } /** * */ - public reset() { + async reset() { this.unCDPaths(); - this.totalBytes = 0; - flushTxMemory(); + await this.chainOperator.reset(); } /** diff --git a/src/core/types/arbitrageloops/skipLoop.ts b/src/core/types/arbitrageloops/skipLoop.ts index f591a7d..d4ee9cf 100644 --- a/src/core/types/arbitrageloops/skipLoop.ts +++ b/src/core/types/arbitrageloops/skipLoop.ts @@ -179,7 +179,7 @@ export class SkipLoop extends MempoolLoop { } if (res.result.code === 0) { - this.chainOperator.client.sequence += 1; + this.chainOperator.client.sequence = this.chainOperator.client.sequence + 1; } await delay(5000); } diff --git a/src/index.ts b/src/index.ts index 5143e2d..d6d8e56 100644 --- a/src/index.ts +++ b/src/index.ts @@ -134,7 +134,7 @@ Total Paths:** \t${paths.length}\n`; await logger.sendMessage("Starting loop...", LogType.All); while (true) { await loop.step(); - loop.reset(); + await loop.reset(); if (startupTime - Date.now() + botConfig.signOfLife * 60000 <= 0) { timeIt++; const mins = (botConfig.signOfLife * timeIt) % 60; From df7195b853252186e6c13dd1310c41626b592ad9 Mon Sep 17 00:00:00 2001 From: 0xBossanova <0xBossanova@proton.me> Date: Wed, 5 Apr 2023 15:47:22 +0200 Subject: [PATCH 2/2] chore: remove while loop on nomempoolloop --- .../types/arbitrageloops/nomempoolLoop.ts | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/core/types/arbitrageloops/nomempoolLoop.ts b/src/core/types/arbitrageloops/nomempoolLoop.ts index c3e57bf..581540e 100644 --- a/src/core/types/arbitrageloops/nomempoolLoop.ts +++ b/src/core/types/arbitrageloops/nomempoolLoop.ts @@ -74,24 +74,20 @@ export class NoMempoolLoop { * */ public async step() { - while (true) { - this.iterations++; - await this.updateStateFunction(this.chainOperator, this.pools); + this.iterations++; + await this.updateStateFunction(this.chainOperator, this.pools); - const arbTrade: OptimalTrade | undefined = this.arbitrageFunction(this.paths, this.botConfig); + const arbTrade: OptimalTrade | undefined = this.arbitrageFunction(this.paths, this.botConfig); - if (arbTrade) { - console.log(inspect(arbTrade.path.pools, { showHidden: true, depth: 4, colors: true })); - console.log(inspect(arbTrade.offerAsset, { showHidden: true, depth: 3, colors: true })); - console.log("expected profit: ", arbTrade.profit); - await this.trade(arbTrade); - this.cdPaths(arbTrade.path); - break; - } - - await delay(1500); + if (arbTrade) { + console.log(inspect(arbTrade.path.pools, { showHidden: true, depth: 4, colors: true })); + console.log(inspect(arbTrade.offerAsset, { showHidden: true, depth: 3, colors: true })); + console.log("expected profit: ", arbTrade.profit); + await this.trade(arbTrade); + this.cdPaths(arbTrade.path); } - return; + + await delay(1500); } /**