diff --git a/apps/extension/src/pages/ibc-swap/index.tsx b/apps/extension/src/pages/ibc-swap/index.tsx index 6282c81610..728165b67c 100644 --- a/apps/extension/src/pages/ibc-swap/index.tsx +++ b/apps/extension/src/pages/ibc-swap/index.tsx @@ -53,7 +53,10 @@ import { useEffectOnce } from "../../hooks/use-effect-once"; import { amountToAmbiguousAverage, amountToAmbiguousString } from "../../utils"; import { Button } from "../../components/button"; import { TextButtonProps } from "../../components/button-text"; -import { UnsignedEVMTransactionWithErc20Approvals } from "@keplr-wallet/stores-eth"; +import { + UnsignedEVMTransaction, + UnsignedEVMTransactionWithErc20Approvals, +} from "@keplr-wallet/stores-eth"; import { EthTxStatus } from "@keplr-wallet/types"; const TextButtonStyles = { @@ -1249,11 +1252,35 @@ export const IBCSwapPage: FunctionComponent = observer(() => { const ethereumAccount = ethereumAccountStore.getAccount( ibcSwapConfigs.amountConfig.chainId ); + + const sender = ibcSwapConfigs.senderConfig.sender; + + const isErc20InCurrency = + ("type" in inCurrency && inCurrency.type === "erc20") || + inCurrency.coinMinimalDenom.startsWith("erc20:"); + const erc20Approval = tx.requiredErc20Approvals?.[0]; + const erc20ApprovalTx = + erc20Approval && isErc20InCurrency + ? ethereumAccount.makeErc20ApprovalTx( + { + ...inCurrency, + type: "erc20", + contractAddress: inCurrency.coinMinimalDenom.replace( + "erc20:", + "" + ), + }, + erc20Approval.spender, + erc20Approval.amount + ) + : undefined; + const { maxFeePerGas, maxPriorityFeePerGas, gasPrice } = ibcSwapConfigs.feeConfig.getEIP1559TxFees( ibcSwapConfigs.feeConfig.type ); - const firstTxFeeObject = + + const feeObject = maxFeePerGas && maxPriorityFeePerGas ? { type: 2, @@ -1275,62 +1302,6 @@ export const IBCSwapPage: FunctionComponent = observer(() => { 16 )}`, }; - const sender = ibcSwapConfigs.senderConfig.sender; - - const isErc20InCurrency = - ("type" in inCurrency && inCurrency.type === "erc20") || - inCurrency.coinMinimalDenom.startsWith("erc20:"); - const erc20Approval = tx.requiredErc20Approvals?.[0]; - const erc20ApprovalTx = - erc20Approval && isErc20InCurrency - ? ethereumAccount.makeErc20ApprovalTx( - { - ...inCurrency, - type: "erc20", - contractAddress: inCurrency.coinMinimalDenom.replace( - "erc20:", - "" - ), - }, - erc20Approval.spender, - erc20Approval.amount - ) - : undefined; - - const secondTxFeeObject = await (async () => { - if (erc20ApprovalTx) { - const { gasUsed } = await ethereumAccount.simulateGas( - sender, - tx - ); - - const { maxFeePerGas, maxPriorityFeePerGas, gasPrice } = - ibcSwapConfigs.feeConfig.getEIP1559TxFees( - ibcSwapConfigs.feeConfig.type - ); - - const feeObject = - maxFeePerGas && maxPriorityFeePerGas - ? { - type: 2, - maxFeePerGas: `0x${Number( - maxFeePerGas.toString() - ).toString(16)}`, - maxPriorityFeePerGas: `0x${Number( - maxPriorityFeePerGas.toString() - ).toString(16)}`, - gasLimit: `0x${gasUsed.toString(16)}`, - } - : { - gasPrice: `0x${Number(gasPrice ?? "0").toString(16)}`, - gasLimit: `0x${gasUsed.toString(16)}`, - }; - - return feeObject; - } - - return {}; - })(); ethereumAccount.setIsSendingTx(true); @@ -1338,7 +1309,7 @@ export const IBCSwapPage: FunctionComponent = observer(() => { sender, { ...(erc20ApprovalTx ?? tx), - ...firstTxFeeObject, + ...feeObject, }, { onBroadcasted: (txHash) => { @@ -1428,132 +1399,171 @@ export const IBCSwapPage: FunctionComponent = observer(() => { }); if (txReceipt.status === EthTxStatus.Success) { + notification.show( + "success", + intl.formatMessage({ + id: "notification.transaction-success", + }), + "" + ); + if (erc20ApprovalTx) { delete (tx as UnsignedEVMTransactionWithErc20Approvals) .requiredErc20Approvals; ethereumAccount.setIsSendingTx(true); - ethereumAccount.sendEthereumTx( - sender, - { - ...(tx as UnsignedEVMTransactionWithErc20Approvals), - ...secondTxFeeObject, - }, - { - onBroadcasted: (txHash) => { - ethereumAccount.setIsSendingTx(false); - - const msg = new RecordTxWithSkipSwapMsg( - inChainId, - outChainId, - { - chainId: outChainId, - denom: outCurrency.coinMinimalDenom, - expectedAmount: - ibcSwapConfigs.amountConfig.outAmount - .toDec() - .toString(), - }, - simpleRoute, - sender, - chainStore.isEvmOnlyChain(outChainId) - ? accountStore.getAccount(outChainId) - .ethereumHexAddress - : accountStore.getAccount(outChainId) - .bech32Address, - [ - ...ibcSwapConfigs.amountConfig.amount.map( - (amount) => { - return { + ethereumAccount + .simulateGas(sender, tx as UnsignedEVMTransaction) + .then(({ gasUsed }) => { + const { + maxFeePerGas, + maxPriorityFeePerGas, + gasPrice, + } = ibcSwapConfigs.feeConfig.getEIP1559TxFees( + ibcSwapConfigs.feeConfig.type + ); + + const feeObject = + maxFeePerGas && maxPriorityFeePerGas + ? { + type: 2, + maxFeePerGas: `0x${BigInt( + maxFeePerGas.truncate().toString() + ).toString(16)}`, + maxPriorityFeePerGas: `0x${BigInt( + maxPriorityFeePerGas.truncate().toString() + ).toString(16)}`, + gasLimit: `0x${gasUsed.toString(16)}`, + } + : { + gasPrice: `0x${BigInt( + gasPrice.truncate().toString() + ).toString(16)}`, + gasLimit: `0x${gasUsed.toString(16)}`, + }; + + ethereumAccount.sendEthereumTx( + sender, + { + ...(tx as UnsignedEVMTransactionWithErc20Approvals), + ...feeObject, + }, + { + onBroadcasted: (txHash) => { + ethereumAccount.setIsSendingTx(false); + + const msg = new RecordTxWithSkipSwapMsg( + inChainId, + outChainId, + { + chainId: outChainId, + denom: outCurrency.coinMinimalDenom, + expectedAmount: + ibcSwapConfigs.amountConfig.outAmount + .toDec() + .toString(), + }, + simpleRoute, + sender, + chainStore.isEvmOnlyChain(outChainId) + ? accountStore.getAccount(outChainId) + .ethereumHexAddress + : accountStore.getAccount(outChainId) + .bech32Address, + [ + ...ibcSwapConfigs.amountConfig.amount.map( + (amount) => { + return { + amount: DecUtils.getTenExponentN( + amount.currency.coinDecimals + ) + .mul(amount.toDec()) + .toString(), + denom: + amount.currency.coinMinimalDenom, + }; + } + ), + { amount: DecUtils.getTenExponentN( - amount.currency.coinDecimals + ibcSwapConfigs.amountConfig.outAmount + .currency.coinDecimals ) - .mul(amount.toDec()) + .mul( + ibcSwapConfigs.amountConfig.outAmount.toDec() + ) .toString(), - denom: amount.currency.coinMinimalDenom, - }; - } - ), - { - amount: DecUtils.getTenExponentN( - ibcSwapConfigs.amountConfig.outAmount - .currency.coinDecimals - ) - .mul( - ibcSwapConfigs.amountConfig.outAmount.toDec() - ) - .toString(), - denom: - ibcSwapConfigs.amountConfig.outAmount - .currency.coinMinimalDenom, - }, - ], - { - currencies: - chainStore.getChain(outChainId).currencies, + denom: + ibcSwapConfigs.amountConfig.outAmount + .currency.coinMinimalDenom, + }, + ], + { + currencies: + chainStore.getChain(outChainId) + .currencies, + }, + routeDurationSeconds ?? 0, + txHash + ); + + new InExtensionMessageRequester().sendMessage( + BACKGROUND_PORT, + msg + ); + + navigate("/", { + replace: true, + }); }, - routeDurationSeconds ?? 0, - txHash - ); - - new InExtensionMessageRequester().sendMessage( - BACKGROUND_PORT, - msg - ); - - navigate("/", { - replace: true, - }); - }, - onFulfill: (txReceipt) => { - const queryBalances = queriesStore.get( - ibcSwapConfigs.amountConfig.chainId - ).queryBalances; - queryBalances - .getQueryEthereumHexAddress(sender) - .balances.forEach((balance) => { + onFulfill: (txReceipt) => { + const queryBalances = queriesStore.get( + ibcSwapConfigs.amountConfig.chainId + ).queryBalances; + queryBalances + .getQueryEthereumHexAddress(sender) + .balances.forEach((balance) => { + if ( + balance.currency.coinMinimalDenom === + ibcSwapConfigs.amountConfig.currency + .coinMinimalDenom || + ibcSwapConfigs.feeConfig.fees.some( + (fee) => + fee.currency.coinMinimalDenom === + balance.currency.coinMinimalDenom + ) + ) { + balance.fetch(); + } + }); + if ( - balance.currency.coinMinimalDenom === - ibcSwapConfigs.amountConfig.currency - .coinMinimalDenom || - ibcSwapConfigs.feeConfig.fees.some( - (fee) => - fee.currency.coinMinimalDenom === - balance.currency.coinMinimalDenom - ) + txReceipt.status === EthTxStatus.Success ) { - balance.fetch(); + notification.show( + "success", + intl.formatMessage({ + id: "notification.transaction-success", + }), + "" + ); + } else { + notification.show( + "failed", + intl.formatMessage({ + id: "error.transaction-failed", + }), + "" + ); } - }); - - if (txReceipt.status === EthTxStatus.Success) { - notification.show( - "success", - intl.formatMessage({ - id: "notification.transaction-success", - }), - "" - ); - } else { - notification.show( - "failed", - intl.formatMessage({ - id: "error.transaction-failed", - }), - "" - ); + }, } - }, - } - ); + ); + }) + .catch((e) => { + console.log(e); + ethereumAccount.setIsSendingTx(false); + }); } - - notification.show( - "success", - intl.formatMessage({ - id: "notification.transaction-success", - }), - "" - ); } else { notification.show( "failed", diff --git a/packages/background/src/recent-send-history/service.ts b/packages/background/src/recent-send-history/service.ts index a41daad20c..f43f7c43de 100644 --- a/packages/background/src/recent-send-history/service.ts +++ b/packages/background/src/recent-send-history/service.ts @@ -1353,9 +1353,12 @@ export class RecentSendHistoryService { const txTracer = new TendermintTxTracer(chainInfo.rpc, "/websocket"); txTracer.addEventListener("error", () => onFulfill(false)); txTracer - .traceTx({ - "tx.hash": history.txHash, - }) + .traceTx( + { + "tx.hash": history.txHash, + }, + true + ) .then((res: any) => { txTracer.close(); diff --git a/packages/cosmos/src/tx-tracer/index.ts b/packages/cosmos/src/tx-tracer/index.ts index eefe42fb34..db555de316 100644 --- a/packages/cosmos/src/tx-tracer/index.ts +++ b/packages/cosmos/src/tx-tracer/index.ts @@ -248,7 +248,8 @@ export class TendermintTxTracer { // Query the tx and subscribe the tx. traceTx( - query: Uint8Array | Record + query: Uint8Array | Record, + noCheckTotalCount?: boolean ): Promise { let resolved = false; return new Promise((resolve) => { @@ -261,7 +262,15 @@ export class TendermintTxTracer { return; } - if (result?.total_count !== "0") { + if (noCheckTotalCount) { + const txs = result.txs + ? result.txs.map((res: any) => res.tx_result || res) + : [result.tx_result || result]; + if (txs.length > 0) { + resolve(result); + return; + } + } else if (result?.total_count !== "0") { resolve(result); return; } @@ -291,10 +300,20 @@ export class TendermintTxTracer { return; } - if (result?.total_count !== "0") { + if (noCheckTotalCount) { + const txs = result.txs + ? result.txs.map((res: any) => res.tx_result || res) + : [result.tx_result || result]; + if (txs.length > 0) { + resolve(result); + return; + } + } else if (result?.total_count !== "0") { resolve(result); return; } + + resolve(result); }) .catch(() => { // noop