From 9e47abaa9f657ba78b58f7d07ec0536c63108673 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Tue, 22 Aug 2023 10:33:27 +0700 Subject: [PATCH 01/11] feat check balance ibc wasm --- src/pages/Balance/helpers.ts | 41 +++++++++++++++++++------ src/pages/Balance/index.tsx | 4 +-- src/pages/UniversalSwap/helpers.ts | 49 +++++++++++++++++++++++++++--- src/tests/universal-swap.spec.ts | 21 +++++++++++++ 4 files changed, 100 insertions(+), 15 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index f0c372927..6339b7a8f 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -1,6 +1,6 @@ import { createWasmAminoConverters, ExecuteResult } from '@cosmjs/cosmwasm-stargate'; import { coin, Coin } from '@cosmjs/proto-signing'; -import { AminoTypes, DeliverTxResponse, GasPrice, SigningStargateClient } from '@cosmjs/stargate'; +import { AminoTypes, DeliverTxResponse, GasPrice, SigningStargateClient, StargateClient } from '@cosmjs/stargate'; import { cosmosTokens, flattenTokens, @@ -22,7 +22,7 @@ import CosmJs, { getExecuteContractMsgs, HandleOptions, parseExecuteContractMult import KawaiiverseJs from 'libs/kawaiiversejs'; import { MsgTransfer } from 'libs/proto/ibc/applications/transfer/v1/tx'; import customRegistry, { customAminoTypes } from 'libs/registry'; -import { buildMultipleMessages, generateError, toAmount } from 'libs/utils'; +import { buildMultipleMessages, generateError, toAmount, toDisplay } from 'libs/utils'; import { generateConvertCw20Erc20Message, generateConvertMsgs, @@ -236,8 +236,25 @@ export const convertTransferIBCErc20Kwt = async ( return result; }; +const getBalanceIBCOraichain = async (to: TokenItemType) => { + if (!to) return { balance: 0 }; + if (to.contractAddress) { + const { balance } = await window.client.queryContractSmart(to.contractAddress, { + balance: { + address: process.env.REACT_APP_IBC_WASM_CONTRACT + } + }); + return { balance: toDisplay(balance) }; + } + const { amount } = await window.client.getBalance(process.env.REACT_APP_IBC_WASM_CONTRACT, to.denom); + return { balance: toDisplay(amount) }; +}; + export const transferEvmToIBC = async ( - from: TokenItemType, + info: { + from: TokenItemType; + to: TokenItemType; + }, fromAmount: number, address: { metamaskAddress?: string; @@ -246,20 +263,26 @@ export const transferEvmToIBC = async ( combinedReceiver?: string ): Promise => { const { metamaskAddress, tronAddress } = address; - const finalTransferAddress = window.Metamask.isTron(from.chainId) ? tronAddress : metamaskAddress; + const finalTransferAddress = window.Metamask.isTron(info.from.chainId) ? tronAddress : metamaskAddress; const oraiAddress = await window.Keplr.getKeplrAddr(); if (!finalTransferAddress || !oraiAddress) throw generateError('Please login both metamask or tronlink and keplr!'); - const gravityContractAddr = gravityContracts[from!.chainId!]; - if (!gravityContractAddr || !from) { + const gravityContractAddr = gravityContracts[info.from!.chainId!]; + if (!gravityContractAddr || !info.from) { throw generateError('No gravity contract addr or no from token'); } - await window.Metamask.checkOrIncreaseAllowance(from, finalTransferAddress, gravityContractAddr, fromAmount); + // check balance ibc wasm + const { balance } = await getBalanceIBCOraichain(info.to); + if (balance < fromAmount) { + throw generateError('Balance IBC Wasm not enough now'); + } + + await window.Metamask.checkOrIncreaseAllowance(info.from, finalTransferAddress, gravityContractAddr, fromAmount); const result = await window.Metamask.transferToGravity( - from, + info.from, fromAmount, finalTransferAddress, - combinedReceiver ?? combineReceiver(oraiAddress, from).combinedReceiver + combinedReceiver ?? combineReceiver(oraiAddress, info.from).combinedReceiver ); return result; }; diff --git a/src/pages/Balance/index.tsx b/src/pages/Balance/index.tsx index c33f29638..effb4625d 100644 --- a/src/pages/Balance/index.tsx +++ b/src/pages/Balance/index.tsx @@ -39,7 +39,7 @@ import StuckOraib from './StuckOraib'; import useGetOraiBridgeBalances from './StuckOraib/useGetOraiBridgeBalances'; import TokenItem from './TokenItem'; -interface BalanceProps {} +interface BalanceProps { } const Balance: React.FC = () => { const [searchParams] = useSearchParams(); @@ -153,7 +153,7 @@ const Balance: React.FC = () => { await handleTransferIBC(from, to, fromAmount); return; } - result = await transferEvmToIBC(from, fromAmount, { metamaskAddress, tronAddress }); + result = await transferEvmToIBC({ from, to }, fromAmount, { metamaskAddress, tronAddress }); console.log('result on click transfer: ', result); processTxResult(from.rpc, result, getTransactionUrl(from.chainId, result.transactionHash)); } catch (ex) { diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 66545d374..6bdcec154 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -220,12 +220,10 @@ export class UniversalSwapHandler { // then find new _toToken in Oraibridge that have same coingeckoId with originalToToken. this._toToken = findToToken(this._toTokenInOrai, this._toToken.chainId); - const ibcInfo: IBCInfo = ibcInfos[this._fromToken.chainId][this._toToken.chainId]; const toAddress = await window.Keplr.getKeplrAddr(this._toToken.chainId); if (!toAddress) throw generateError('Please login keplr!'); - const transferAddress = this.getTranferAddress(metamaskAddress, tronAddress, ibcInfo); - const ibcMemo = this.getIbcMemo(transferAddress); + const { ibcInfo, ibcMemo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); // create bridge msg const msgTransfer = this.generateMsgsTransferOraiToEvm(ibcInfo, toAddress, ibcMemo); @@ -233,6 +231,43 @@ export class UniversalSwapHandler { return [...msgExecuteSwap, ...msgExecuteTransfer]; } + getIbcInfoIbcMemo(metamaskAddress: string, tronAddress: string) { + const ibcInfo: IBCInfo = ibcInfos[this._fromToken.chainId][this._toToken.chainId]; + const transferAddress = this.getTranferAddress(metamaskAddress, tronAddress, ibcInfo); + const ibcMemo = this.getIbcMemo(transferAddress); + return { + ibcInfo, + ibcMemo + }; + } + + async checkBalanceChannelIbc(metamaskAddress: string, tronAddress: string) { + const ics20Contract = new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); + const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); + const { balances } = await ics20Contract.channel({ + forward: false, + id: ibcInfo.channel + }); + + for (let balance of balances) { + if ( + 'native' in balance && + balance.native.denom === `${ibcInfo.source}/${ibcInfo.channel}/${this._toToken.denom}` + ) { + const pairMapping = await ics20Contract.pairMapping({ key: balance.native.denom }); + const trueBalance = toDisplay(balance.native.amount, pairMapping.pair_mapping.remote_decimals); + const _toAmount = toDisplay(this._simulateAmount, this._toToken.decimals); + if (trueBalance < _toAmount) { + throw new Error(`${ibcInfo.channel}/${this._toToken.denom} is not enough balance!`); + } + return; + } else { + // do nothing because currently we dont have any cw20 balance in the channel + } + } + return; + } + async swap(): Promise { const messages = this.generateMsgsSwap(); const result = await CosmJs.executeMultiple({ @@ -256,6 +291,7 @@ export class UniversalSwapHandler { this._toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === this._toToken.coinGeckoId); const combinedMsgs = await this.combineMsgs(metamaskAddress, tronAddress); + await this.checkBalanceChannelIbc(metamaskAddress, tronAddress); // handle sign and broadcast transactions const offlineSigner = await window.Keplr.getOfflineSigner(this._fromToken.chainId); @@ -273,7 +309,12 @@ export class UniversalSwapHandler { } async transferAndSwap(combinedReceiver: string, metamaskAddress?: string, tronAddress?: string): Promise { - return transferEvmToIBC(this._fromToken, this._fromAmount, { metamaskAddress, tronAddress }, combinedReceiver); + return transferEvmToIBC( + { from: this._fromToken, to: this._toToken }, + this._fromAmount, + { metamaskAddress, tronAddress }, + combinedReceiver + ); } async processUniversalSwap(combinedReceiver: string, universalSwapType: UniversalSwapType, swapData: SwapData) { diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index 6e62b75b6..69d40a67c 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -855,6 +855,27 @@ describe('universal-swap', () => { expect(error?.ex?.message).toEqual('Please login keplr!'); } }); + + it.each([ + [ + 'get-ibc-info-ibc-memo', + 'airight', + 'oraibridge-subnet-2', + { + ibcInfo: { + source: `wasm.${process.env.REACT_APP_IBC_WASM_CONTRACT}`, + channel: 'channel-29', + timeout: 3600 + }, + ibcMemo: 'oraib0x1234' + } + ] + ])('test-get-ibc-info-ibc-memo', async (_name: string, toCoingeckoId, toChainId, expectedTransferMsg) => { + universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId); + universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); + const msg = await universalSwap.getIbcInfoIbcMemo('0x1234', 'T1234'); + expect(msg).toEqual(expectedTransferMsg); + }); }); it.each([ From cdbdb60852d6b959c14d08feb72b0def7d40289e Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Tue, 22 Aug 2023 11:31:17 +0700 Subject: [PATCH 02/11] refactored for ez testing --- src/pages/Balance/helpers.ts | 23 +++++++++++++---------- src/pages/UniversalSwap/helpers.ts | 20 ++++++++++++++++---- src/tests/universal-swap.spec.ts | 4 ++-- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index 6339b7a8f..ef6ab0f54 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -17,7 +17,11 @@ import { network } from 'config/networks'; import { calculateTimeoutTimestamp, getNetworkGasPrice } from 'helper'; import { CwIcs20LatestClient, TransferBackMsg } from '@oraichain/common-contracts-sdk'; -import { OraiswapTokenClient } from '@oraichain/oraidex-contracts-sdk'; +import { + OraiswapTokenClient, + OraiswapTokenQueryClient, + OraiswapTokenReadOnlyInterface +} from '@oraichain/oraidex-contracts-sdk'; import CosmJs, { getExecuteContractMsgs, HandleOptions, parseExecuteContractMultiple } from 'libs/cosmjs'; import KawaiiverseJs from 'libs/kawaiiversejs'; import { MsgTransfer } from 'libs/proto/ibc/applications/transfer/v1/tx'; @@ -236,18 +240,15 @@ export const convertTransferIBCErc20Kwt = async ( return result; }; -const getBalanceIBCOraichain = async (to: TokenItemType) => { +const getBalanceIBCOraichain = async (to: TokenItemType, tokenQueryClient?: OraiswapTokenReadOnlyInterface) => { if (!to) return { balance: 0 }; if (to.contractAddress) { - const { balance } = await window.client.queryContractSmart(to.contractAddress, { - balance: { - address: process.env.REACT_APP_IBC_WASM_CONTRACT - } - }); - return { balance: toDisplay(balance) }; + const cw20Token = tokenQueryClient ?? new OraiswapTokenQueryClient(window.client, to.contractAddress); + const { balance } = await cw20Token.balance({ address: process.env.REACT_APP_IBC_WASM_CONTRACT }); + return { balance: toDisplay(balance, to.decimals) }; } const { amount } = await window.client.getBalance(process.env.REACT_APP_IBC_WASM_CONTRACT, to.denom); - return { balance: toDisplay(amount) }; + return { balance: toDisplay(amount, to.decimals) }; }; export const transferEvmToIBC = async ( @@ -274,7 +275,9 @@ export const transferEvmToIBC = async ( // check balance ibc wasm const { balance } = await getBalanceIBCOraichain(info.to); if (balance < fromAmount) { - throw generateError('Balance IBC Wasm not enough now'); + throw generateError( + `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${fromAmount}, have ${balance}` + ); } await window.Metamask.checkOrIncreaseAllowance(info.from, finalTransferAddress, gravityContractAddr, fromAmount); diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 6bdcec154..65b5988d2 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -8,7 +8,14 @@ import { ORAI, ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX } from 'config/constants'; import { ibcInfos, oraichain2oraib } from 'config/ibcInfos'; import { network } from 'config/networks'; import { calculateTimeoutTimestamp, getNetworkGasPrice, tronToEthAddress } from 'helper'; -import { CwIcs20LatestQueryClient, Ratio, TransferBackMsg, Uint128 } from '@oraichain/common-contracts-sdk'; +import { + CwIcs20LatestInterface, + CwIcs20LatestQueryClient, + CwIcs20LatestReadOnlyInterface, + Ratio, + TransferBackMsg, + Uint128 +} from '@oraichain/common-contracts-sdk'; import CosmJs, { getExecuteContractMsgs, parseExecuteContractMultiple } from 'libs/cosmjs'; import { MsgTransfer } from 'libs/proto/ibc/applications/transfer/v1/tx'; import customRegistry, { customAminoTypes } from 'libs/registry'; @@ -71,6 +78,7 @@ export class UniversalSwapHandler { private _fromAmount: number; private _simulateAmount: string; private _userSlippage?: number; + private _cwIcs20LatestClient: CwIcs20LatestInterface; constructor( sender?: string, @@ -78,7 +86,8 @@ export class UniversalSwapHandler { toToken?: TokenItemType, fromAmount?: number, simulateAmount?: string, - userSlippage?: number + userSlippage?: number, + cwIcs20LatestClient?: CwIcs20LatestInterface ) { this._sender = sender; this._fromToken = fromToken; @@ -86,6 +95,7 @@ export class UniversalSwapHandler { this._fromAmount = fromAmount; this._simulateAmount = simulateAmount; this._userSlippage = userSlippage; + this._cwIcs20LatestClient = cwIcs20LatestClient; } get fromToken() { @@ -232,6 +242,7 @@ export class UniversalSwapHandler { } getIbcInfoIbcMemo(metamaskAddress: string, tronAddress: string) { + if (!ibcInfos[this._fromToken.chainId]) throw generateError('Cannot find ibc info'); const ibcInfo: IBCInfo = ibcInfos[this._fromToken.chainId][this._toToken.chainId]; const transferAddress = this.getTranferAddress(metamaskAddress, tronAddress, ibcInfo); const ibcMemo = this.getIbcMemo(transferAddress); @@ -242,7 +253,8 @@ export class UniversalSwapHandler { } async checkBalanceChannelIbc(metamaskAddress: string, tronAddress: string) { - const ics20Contract = new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); + const ics20Contract = + this._cwIcs20LatestClient ?? new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); const { balances } = await ics20Contract.channel({ forward: false, @@ -258,7 +270,7 @@ export class UniversalSwapHandler { const trueBalance = toDisplay(balance.native.amount, pairMapping.pair_mapping.remote_decimals); const _toAmount = toDisplay(this._simulateAmount, this._toToken.decimals); if (trueBalance < _toAmount) { - throw new Error(`${ibcInfo.channel}/${this._toToken.denom} is not enough balance!`); + throw generateError(`${ibcInfo.channel}/${this._toToken.denom} is not enough balance!`); } return; } else { diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index 69d40a67c..d3fe92b00 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -870,10 +870,10 @@ describe('universal-swap', () => { ibcMemo: 'oraib0x1234' } ] - ])('test-get-ibc-info-ibc-memo', async (_name: string, toCoingeckoId, toChainId, expectedTransferMsg) => { + ])('test-get-ibc-info-ibc-memo', (_name: string, toCoingeckoId, toChainId, expectedTransferMsg) => { universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId); universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); - const msg = await universalSwap.getIbcInfoIbcMemo('0x1234', 'T1234'); + const msg = universalSwap.getIbcInfoIbcMemo('0x1234', 'T1234'); expect(msg).toEqual(expectedTransferMsg); }); }); From b31cff10c36ef9a4c8948b5118cfdc4e5f2d60de Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Tue, 22 Aug 2023 11:59:21 +0700 Subject: [PATCH 03/11] simplify universal swap handler get set --- src/pages/UniversalSwap/helpers.ts | 180 +++++++++-------------------- src/tests/universal-swap.spec.ts | 31 +++-- 2 files changed, 78 insertions(+), 133 deletions(-) diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 65b5988d2..db96733a1 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -71,87 +71,16 @@ export const checkEvmAddress = (chainId: NetworkChainId, metamaskAddress?: strin }; export class UniversalSwapHandler { - private _sender: string; - private _fromToken: TokenItemType; - private _toToken: TokenItemType; - private _toTokenInOrai: TokenItemType; - private _fromAmount: number; - private _simulateAmount: string; - private _userSlippage?: number; - private _cwIcs20LatestClient: CwIcs20LatestInterface; - + public toTokenInOrai: TokenItemType; constructor( - sender?: string, - fromToken?: TokenItemType, - toToken?: TokenItemType, - fromAmount?: number, - simulateAmount?: string, - userSlippage?: number, - cwIcs20LatestClient?: CwIcs20LatestInterface - ) { - this._sender = sender; - this._fromToken = fromToken; - this._toToken = toToken; - this._fromAmount = fromAmount; - this._simulateAmount = simulateAmount; - this._userSlippage = userSlippage; - this._cwIcs20LatestClient = cwIcs20LatestClient; - } - - get fromToken() { - return this._fromToken; - } - - get toToken() { - return this._toToken; - } - - get toTokenInOrai() { - return this._toTokenInOrai; - } - - get sender() { - return this._sender; - } - - get fromAmount() { - return this._fromAmount; - } - - get simulateAmount() { - return this._simulateAmount; - } - - get userSlippage() { - return this._userSlippage; - } - - set fromToken(from: TokenItemType) { - this._fromToken = from; - } - - set toToken(to: TokenItemType) { - this._toToken = to; - } - - set toTokenInOrai(toTokenInOrai: TokenItemType) { - this._toTokenInOrai = toTokenInOrai; - } - - set sender(sender: string) { - this._sender = sender; - } - set fromAmount(fromAmount: number) { - this._fromAmount = fromAmount; - } - - set simulateAmount(simulateAmount: string) { - this._simulateAmount = simulateAmount; - } - - set userSlippage(userSlippage: number) { - this._userSlippage = userSlippage; - } + public sender: string, + public fromToken: TokenItemType, + public toToken: TokenItemType, + public fromAmount: number, + public simulateAmount: string, + public userSlippage: number, + public cwIcs20LatestClient?: CwIcs20LatestInterface + ) {} calculateMinReceive(simulateAmount: string, userSlippage: number, decimals: number): Uint128 { const amount = toDisplay(simulateAmount, decimals); @@ -171,18 +100,18 @@ export class UniversalSwapHandler { * @returns combined messages */ async combineMsgCosmos(): Promise { - const ibcInfo: IBCInfo = ibcInfos[this._fromToken.chainId][this._toToken.chainId]; - const toAddress = await window.Keplr.getKeplrAddr(this._toToken.chainId); + const ibcInfo: IBCInfo = ibcInfos[this.fromToken.chainId][this.toToken.chainId]; + const toAddress = await window.Keplr.getKeplrAddr(this.toToken.chainId); if (!toAddress) throw generateError('Please login keplr!'); - const amount = coin(this._simulateAmount, this._toTokenInOrai.denom); + const amount = coin(this.simulateAmount, this.toTokenInOrai.denom); const msgTransfer = { typeUrl: '/ibc.applications.transfer.v1.MsgTransfer', value: MsgTransfer.fromPartial({ sourcePort: ibcInfo.source, sourceChannel: ibcInfo.channel, token: amount, - sender: this._sender, + sender: this.sender, receiver: toAddress, memo: '', timeoutTimestamp: calculateTimeoutTimestamp(ibcInfo.timeout) @@ -190,9 +119,9 @@ export class UniversalSwapHandler { }; // if not same coingeckoId, swap first then transfer token that have same coingeckoid. - if (this._fromToken.coinGeckoId !== this._toToken.coinGeckoId) { + if (this.fromToken.coinGeckoId !== this.toToken.coinGeckoId) { const msgSwap = this.generateMsgsSwap(); - const msgExecuteSwap = getExecuteContractMsgs(this._sender, parseExecuteContractMultiple(msgSwap)); + const msgExecuteSwap = getExecuteContractMsgs(this.sender, parseExecuteContractMultiple(msgSwap)); return [...msgExecuteSwap, msgTransfer]; } return [msgTransfer]; @@ -201,18 +130,18 @@ export class UniversalSwapHandler { getTranferAddress(metamaskAddress: string, tronAddress: string, ibcInfo: IBCInfo) { let transferAddress = metamaskAddress; // check tron network and convert address - if (this._toToken.prefix === ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX) { + if (this.toToken.prefix === ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX) { transferAddress = tronToEthAddress(tronAddress); } // only allow transferring back to ethereum / bsc only if there's metamask address and when the metamask address is used, which is in the ibcMemo variable - if (!transferAddress && (this._toTokenInOrai.evmDenoms || ibcInfo.channel === oraichain2oraib)) { + if (!transferAddress && (this.toTokenInOrai.evmDenoms || ibcInfo.channel === oraichain2oraib)) { throw generateError('Please login metamask / tronlink!'); } return transferAddress; } getIbcMemo(transferAddress: string) { - return this._toToken.chainId === 'oraibridge-subnet-2' ? this._toToken.prefix + transferAddress : ''; + return this.toToken.chainId === 'oraibridge-subnet-2' ? this.toToken.prefix + transferAddress : ''; } /** @@ -222,28 +151,28 @@ export class UniversalSwapHandler { async combineMsgEvm(metamaskAddress: string, tronAddress: string) { let msgExecuteSwap: EncodeObject[] = []; // if from and to dont't have same coingeckoId, create swap msg to combine with bridge msg - if (this._fromToken.coinGeckoId !== this._toToken.coinGeckoId) { + if (this.fromToken.coinGeckoId !== this.toToken.coinGeckoId) { const msgSwap = this.generateMsgsSwap(); - msgExecuteSwap = getExecuteContractMsgs(this._sender, parseExecuteContractMultiple(msgSwap)); + msgExecuteSwap = getExecuteContractMsgs(this.sender, parseExecuteContractMultiple(msgSwap)); } // then find new _toToken in Oraibridge that have same coingeckoId with originalToToken. - this._toToken = findToToken(this._toTokenInOrai, this._toToken.chainId); + this.toToken = findToToken(this.toTokenInOrai, this.toToken.chainId); - const toAddress = await window.Keplr.getKeplrAddr(this._toToken.chainId); + const toAddress = await window.Keplr.getKeplrAddr(this.toToken.chainId); if (!toAddress) throw generateError('Please login keplr!'); const { ibcInfo, ibcMemo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); // create bridge msg const msgTransfer = this.generateMsgsTransferOraiToEvm(ibcInfo, toAddress, ibcMemo); - const msgExecuteTransfer = getExecuteContractMsgs(this._sender, parseExecuteContractMultiple(msgTransfer)); + const msgExecuteTransfer = getExecuteContractMsgs(this.sender, parseExecuteContractMultiple(msgTransfer)); return [...msgExecuteSwap, ...msgExecuteTransfer]; } getIbcInfoIbcMemo(metamaskAddress: string, tronAddress: string) { - if (!ibcInfos[this._fromToken.chainId]) throw generateError('Cannot find ibc info'); - const ibcInfo: IBCInfo = ibcInfos[this._fromToken.chainId][this._toToken.chainId]; + if (!ibcInfos[this.fromToken.chainId]) throw generateError('Cannot find ibc info'); + const ibcInfo: IBCInfo = ibcInfos[this.fromToken.chainId][this.toToken.chainId]; const transferAddress = this.getTranferAddress(metamaskAddress, tronAddress, ibcInfo); const ibcMemo = this.getIbcMemo(transferAddress); return { @@ -252,9 +181,13 @@ export class UniversalSwapHandler { }; } - async checkBalanceChannelIbc(metamaskAddress: string, tronAddress: string) { + async checkBalanceChannelIbc( + metamaskAddress: string, + tronAddress: string, + cwIcs20LatestClient?: CwIcs20LatestReadOnlyInterface + ) { const ics20Contract = - this._cwIcs20LatestClient ?? new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); + this.cwIcs20LatestClient ?? new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); const { balances } = await ics20Contract.channel({ forward: false, @@ -264,13 +197,13 @@ export class UniversalSwapHandler { for (let balance of balances) { if ( 'native' in balance && - balance.native.denom === `${ibcInfo.source}/${ibcInfo.channel}/${this._toToken.denom}` + balance.native.denom === `${ibcInfo.source}/${ibcInfo.channel}/${this.toToken.denom}` ) { const pairMapping = await ics20Contract.pairMapping({ key: balance.native.denom }); const trueBalance = toDisplay(balance.native.amount, pairMapping.pair_mapping.remote_decimals); - const _toAmount = toDisplay(this._simulateAmount, this._toToken.decimals); + const _toAmount = toDisplay(this.simulateAmount, this.toToken.decimals); if (trueBalance < _toAmount) { - throw generateError(`${ibcInfo.channel}/${this._toToken.denom} is not enough balance!`); + throw generateError(`${ibcInfo.channel}/${this.toToken.denom} is not enough balance!`); } return; } else { @@ -292,38 +225,37 @@ export class UniversalSwapHandler { } async combineMsgs(metamaskAddress: string, tronAddress: string): Promise { - if (this._toToken.chainId === 'cosmoshub-4' || this._toToken.chainId === 'osmosis-1') - return this.combineMsgCosmos(); + if (this.toToken.chainId === 'cosmoshub-4' || this.toToken.chainId === 'osmosis-1') return this.combineMsgCosmos(); return this.combineMsgEvm(metamaskAddress, tronAddress); } // Universal swap from Oraichain to cosmos-hub | osmosis | EVM networks. async swapAndTransfer({ metamaskAddress, tronAddress }: SwapData): Promise { - // find to token in Oraichain to swap first and use this._toTokenInOrai as fromToken in bridge message. - this._toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === this._toToken.coinGeckoId); + // find to token in Oraichain to swap first and use this.toTokenInOrai as fromToken in bridge message. + this.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === this.toToken.coinGeckoId); const combinedMsgs = await this.combineMsgs(metamaskAddress, tronAddress); await this.checkBalanceChannelIbc(metamaskAddress, tronAddress); // handle sign and broadcast transactions - const offlineSigner = await window.Keplr.getOfflineSigner(this._fromToken.chainId); + const offlineSigner = await window.Keplr.getOfflineSigner(this.fromToken.chainId); const aminoTypes = new AminoTypes({ ...createWasmAminoConverters(), ...customAminoTypes }); - const client = await SigningStargateClient.connectWithSigner(this._fromToken.rpc, offlineSigner, { + const client = await SigningStargateClient.connectWithSigner(this.fromToken.rpc, offlineSigner, { registry: customRegistry, aminoTypes, gasPrice: GasPrice.fromString(`${await getNetworkGasPrice()}${network.denom}`) }); - const result = await client.signAndBroadcast(this._sender, combinedMsgs, 'auto'); + const result = await client.signAndBroadcast(this.sender, combinedMsgs, 'auto'); return result; } async transferAndSwap(combinedReceiver: string, metamaskAddress?: string, tronAddress?: string): Promise { return transferEvmToIBC( - { from: this._fromToken, to: this._toToken }, - this._fromAmount, + { from: this.fromToken, to: this.toToken }, + this.fromAmount, { metamaskAddress, tronAddress }, combinedReceiver ); @@ -337,19 +269,15 @@ export class UniversalSwapHandler { generateMsgsSwap() { try { - const _fromAmount = toAmount(this._fromAmount, this._fromToken.decimals).toString(); + const _fromAmount = toAmount(this.fromAmount, this.fromToken.decimals).toString(); - const minimumReceive = this.calculateMinReceive( - this._simulateAmount, - this._userSlippage, - this._fromToken.decimals - ); + const minimumReceive = this.calculateMinReceive(this.simulateAmount, this.userSlippage, this.fromToken.decimals); const msgs = generateContractMessages({ type: Type.SWAP, - sender: this._sender, + sender: this.sender, amount: _fromAmount, - fromInfo: this._fromToken!, - toInfo: this._toTokenInOrai ?? this._toToken, + fromInfo: this.fromToken!, + toInfo: this.toTokenInOrai ?? this.toToken, minimumReceive } as SwapQuery); const msg = msgs[0]; @@ -371,7 +299,7 @@ export class UniversalSwapHandler { */ generateMsgsTransferOraiToEvm(ibcInfo: IBCInfo, toAddress: string, ibcMemo: string) { try { - const { info: assetInfo } = parseTokenInfo(this._toTokenInOrai); + const { info: assetInfo } = parseTokenInfo(this.toTokenInOrai); const ibcWasmContractAddress = ibcInfo.source.split('.')[1]; if (!ibcWasmContractAddress) @@ -380,7 +308,7 @@ export class UniversalSwapHandler { const msg: TransferBackMsg = { local_channel_id: ibcInfo.channel, remote_address: toAddress, - remote_denom: this._toToken.denom, + remote_denom: this.toToken.denom, timeout: ibcInfo.timeout, memo: ibcMemo }; @@ -394,8 +322,8 @@ export class UniversalSwapHandler { const msgs = { contract: ibcWasmContractAddress, msg: Buffer.from(JSON.stringify(executeMsgSend)), - sender: this._sender, - sent_funds: [{ amount: this._simulateAmount, denom: ORAI }] + sender: this.sender, + sent_funds: [{ amount: this.simulateAmount, denom: ORAI }] }; return buildMultipleMessages(msgs); } @@ -403,7 +331,7 @@ export class UniversalSwapHandler { const executeMsgSend = { send: { contract: ibcWasmContractAddress, - amount: this._simulateAmount, + amount: this.simulateAmount, msg: cosmwasm.toBinary(msg) } }; @@ -411,9 +339,9 @@ export class UniversalSwapHandler { // generate contract message for CW20 token in Oraichain. // Example: tranfer USDT/Oraichain -> AIRI/BSC. _toTokenInOrai is AIRI in Oraichain. const msgs = { - contract: this._toTokenInOrai.contractAddress, + contract: this.toTokenInOrai.contractAddress, msg: Buffer.from(JSON.stringify(executeMsgSend)), - sender: this._sender, + sender: this.sender, sent_funds: [] }; return buildMultipleMessages(msgs); diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index d3fe92b00..5486c41ea 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -1,4 +1,4 @@ -import { toBinary } from '@cosmjs/cosmwasm-stargate'; +import { CosmWasmClient, SigningCosmWasmClient, toBinary } from '@cosmjs/cosmwasm-stargate'; import { toUtf8 } from '@cosmjs/encoding'; import { TokenItemType, UniversalSwapType, cosmosTokens, flattenTokens, oraichainTokens } from 'config/bridgeTokens'; import { CoinGeckoId, CosmosChainId, EvmChainId, NetworkChainId } from 'config/chainInfos'; @@ -22,6 +22,8 @@ import { Type, generateContractMessages, simulateSwap } from 'rest/api'; import * as restApi from 'rest/api'; import { IBCInfo } from 'types/ibc'; import { senderAddress } from './common'; +import { CwIcs20LatestClient } from '@oraichain/common-contracts-sdk'; +import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; describe('universal-swap', () => { let windowSpy: jest.SpyInstance; @@ -89,7 +91,7 @@ describe('universal-swap', () => { const expectedMinimumReceive = '990000'; const userSlippage = 1; - const universalSwap = new UniversalSwapHandler(); + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); const minimumReceive = universalSwap.calculateMinReceive(simulateData.amount, userSlippage, 6); it('return expected minimum receive', () => { @@ -351,7 +353,7 @@ describe('universal-swap', () => { expect(result.combinedReceiver).toEqual(`${oraib2oraichain}/receiver:foobar:orai`); }); it('test-getUniversalSwapToAddress', async () => { - const universalSwap = new UniversalSwapHandler(); + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); windowSpy.mockImplementation(() => ({ Metamask: { getEthAddress: () => { @@ -371,7 +373,7 @@ describe('universal-swap', () => { }); describe('test-processUniversalSwap-with-mock', () => { - const universalSwap = new UniversalSwapHandler(); + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); const fromAmount = '100000'; const simulateAmount = '100'; const userSlippage = 0.01; @@ -390,6 +392,21 @@ describe('universal-swap', () => { transferAndSwapSpy = jest.spyOn(universalSwap, 'transferAndSwap'); }); + it('test-universal-swap-constructor', async () => { + const universalSwap = new UniversalSwapHandler('foo', oraichainTokens[0], oraichainTokens[0], 1, '1', 1); + expect(universalSwap.sender).toEqual('foo'); + expect(universalSwap.fromToken).toBeDefined(); + expect(universalSwap.toToken).toBeDefined(); + expect(universalSwap.fromAmount).toEqual(1); + expect(universalSwap.simulateAmount).toEqual('1'); + expect(universalSwap.userSlippage).toEqual(1); + expect(universalSwap.cwIcs20LatestClient).not.toBeDefined(); + const wallet = await DirectSecp256k1HdWallet.generate(); + const client = await SigningCosmWasmClient.connectWithSigner('http://rpc.orai.io', wallet); + universalSwap.cwIcs20LatestClient = new CwIcs20LatestClient(client, 'sender', 'foo'); + expect(universalSwap.cwIcs20LatestClient).toBeDefined(); + }); + it.each([ ['cosmos-hub-network', 'cosmos', 'cosmoshub-4', 'cosmos', 'cosmoshub-4'], ['osmosis-network', 'osmosis', 'osmosis-1', 'osmosis', 'osmosis-1'], @@ -434,7 +451,7 @@ describe('universal-swap', () => { ['Oraichain', 'evm'], ['0x01', 'evm'] ])('test-combine-msgs-logic', async (chainId, expectedMsgType) => { - const universalSwap = new UniversalSwapHandler(); + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); const toToken = flattenTokens.find((item) => item.coinGeckoId === 'tether'); universalSwap.toToken = toToken; universalSwap.toToken.chainId = chainId; @@ -907,7 +924,7 @@ describe('universal-swap', () => { ])( 'test get transfer address should return transferAddress correctly & throw error correctly', (metamaskAddress: string, tronAddress: string, toToken: TokenItemType, expectedTransferAddr: string) => { - const universalSwap = new UniversalSwapHandler(); + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); const ibcInfo = ibcInfos['Oraichain']['oraibridge-subnet-2']; universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === 'airight'); universalSwap.toToken = toToken; @@ -926,7 +943,7 @@ describe('universal-swap', () => { ])( 'test getIbcMemo should return ibc memo correctly', (transferAddress: string, toToken: TokenItemType, expectedIbcMemo: string) => { - const universalSwap = new UniversalSwapHandler(); + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); universalSwap.toToken = toToken; const ibcMemo = universalSwap.getIbcMemo(transferAddress); expect(ibcMemo).toEqual(expectedIbcMemo); From c17843f6da5097c7ca002c16f9a20b3401751b67 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Wed, 23 Aug 2023 14:06:27 +0700 Subject: [PATCH 04/11] update logic check balance evm --- src/pages/Balance/helpers.ts | 74 ++++++++++--- src/pages/UniversalSwap/helpers.ts | 20 ++-- src/tests/universal-swap.spec.ts | 169 ++++++++++++++++++++++++++++- 3 files changed, 234 insertions(+), 29 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index ef6ab0f54..a55aed849 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -6,6 +6,7 @@ import { flattenTokens, gravityContracts, kawaiiTokens, + oraichainTokens, TokenItemType, tokenMap, UniversalSwapType @@ -240,17 +241,60 @@ export const convertTransferIBCErc20Kwt = async ( return result; }; -const getBalanceIBCOraichain = async (to: TokenItemType, tokenQueryClient?: OraiswapTokenReadOnlyInterface) => { - if (!to) return { balance: 0 }; - if (to.contractAddress) { - const cw20Token = tokenQueryClient ?? new OraiswapTokenQueryClient(window.client, to.contractAddress); - const { balance } = await cw20Token.balance({ address: process.env.REACT_APP_IBC_WASM_CONTRACT }); - return { balance: toDisplay(balance, to.decimals) }; +const getBalanceIBCOraichain = async (token: TokenItemType, tokenQueryClient?: OraiswapTokenReadOnlyInterface) => { + if (!token) return { balance: 0 }; + try { + if (token.contractAddress) { + const cw20Token = tokenQueryClient ?? new OraiswapTokenQueryClient(window.client, token.contractAddress); + const { balance } = await cw20Token.balance({ address: process.env.REACT_APP_IBC_WASM_CONTRACT }); + return { balance: toDisplay(balance, token.decimals) }; + } + const { amount } = await window.client.getBalance(process.env.REACT_APP_IBC_WASM_CONTRACT, token.denom); + return { balance: toDisplay(amount, token.decimals) }; + } catch (error) { + console.log('error: ', error); } - const { amount } = await window.client.getBalance(process.env.REACT_APP_IBC_WASM_CONTRACT, to.denom); - return { balance: toDisplay(amount, to.decimals) }; }; +const checkBalanceIBCOraichain = async ( + to: TokenItemType, + from: TokenItemType, + amount: { + toAmount: string; + fromAmount: number; + } +) => { + let tokens; + // ORAI ( ETH ) -> check ORAI (ORAICHAIN) -> ORAI (BSC) + if (to.coinGeckoId === from.coinGeckoId) { + tokens = oraichainTokens.find((t) => t.coinGeckoId === from.coinGeckoId); + if (!tokens) return 0; + const { balance } = await getBalanceIBCOraichain(tokens); + if (balance < amount.fromAmount) { + throw generateError( + `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.fromAmount}, have ${balance}` + ); + } + return balance; + } + // ORAI ( ETH ) -> check ORAI (ORAICHAIN) (fromAmount) -> check AIRI (ORAICHAIN) (toAmount) -> AIRI (BSC) + // ORAI ( ETH ) -> check ORAI (ORAICHAIN) (fromAmount) -> check wTRX (ORAICHAIN) (toAmount) -> wTRX (TRON) + tokens = oraichainTokens.filter((t) => t.coinGeckoId === to.coinGeckoId || t.coinGeckoId === from.coinGeckoId); + for (const token of tokens) { + const { balance } = await getBalanceIBCOraichain(token); + if (token.coinGeckoId === to.coinGeckoId && balance < toDisplay(amount.toAmount, to.decimals)) { + throw generateError( + `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.toAmount}, have ${balance}` + ); + } + if (token.coinGeckoId === from.coinGeckoId && balance < amount.fromAmount) { + throw generateError( + `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.fromAmount}, have ${balance}` + ); + } + } + return 0; +}; export const transferEvmToIBC = async ( info: { from: TokenItemType; @@ -261,7 +305,8 @@ export const transferEvmToIBC = async ( metamaskAddress?: string; tronAddress?: string; }, - combinedReceiver?: string + combinedReceiver?: string, + simulateAmount?: string ): Promise => { const { metamaskAddress, tronAddress } = address; const finalTransferAddress = window.Metamask.isTron(info.from.chainId) ? tronAddress : metamaskAddress; @@ -273,12 +318,11 @@ export const transferEvmToIBC = async ( } // check balance ibc wasm - const { balance } = await getBalanceIBCOraichain(info.to); - if (balance < fromAmount) { - throw generateError( - `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${fromAmount}, have ${balance}` - ); - } + simulateAmount && + (await checkBalanceIBCOraichain(info.to, info.from, { + fromAmount, + toAmount: simulateAmount + })); await window.Metamask.checkOrIncreaseAllowance(info.from, finalTransferAddress, gravityContractAddr, fromAmount); const result = await window.Metamask.transferToGravity( diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index db96733a1..1c77cd34e 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -79,7 +79,7 @@ export class UniversalSwapHandler { public fromAmount: number, public simulateAmount: string, public userSlippage: number, - public cwIcs20LatestClient?: CwIcs20LatestInterface + public cwIcs20LatestClient?: CwIcs20LatestInterface | CwIcs20LatestReadOnlyInterface ) {} calculateMinReceive(simulateAmount: string, userSlippage: number, decimals: number): Uint128 { @@ -181,14 +181,9 @@ export class UniversalSwapHandler { }; } - async checkBalanceChannelIbc( - metamaskAddress: string, - tronAddress: string, - cwIcs20LatestClient?: CwIcs20LatestReadOnlyInterface - ) { + async checkBalanceChannelIbc(ibcInfo: IBCInfo) { const ics20Contract = this.cwIcs20LatestClient ?? new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); - const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); const { balances } = await ics20Contract.channel({ forward: false, id: ibcInfo.channel @@ -201,16 +196,15 @@ export class UniversalSwapHandler { ) { const pairMapping = await ics20Contract.pairMapping({ key: balance.native.denom }); const trueBalance = toDisplay(balance.native.amount, pairMapping.pair_mapping.remote_decimals); - const _toAmount = toDisplay(this.simulateAmount, this.toToken.decimals); + const _toAmount = toDisplay(this.simulateAmount); if (trueBalance < _toAmount) { throw generateError(`${ibcInfo.channel}/${this.toToken.denom} is not enough balance!`); } - return; } else { // do nothing because currently we dont have any cw20 balance in the channel } } - return; + return false; } async swap(): Promise { @@ -235,7 +229,8 @@ export class UniversalSwapHandler { this.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === this.toToken.coinGeckoId); const combinedMsgs = await this.combineMsgs(metamaskAddress, tronAddress); - await this.checkBalanceChannelIbc(metamaskAddress, tronAddress); + const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); + await this.checkBalanceChannelIbc(ibcInfo); // handle sign and broadcast transactions const offlineSigner = await window.Keplr.getOfflineSigner(this.fromToken.chainId); @@ -257,7 +252,8 @@ export class UniversalSwapHandler { { from: this.fromToken, to: this.toToken }, this.fromAmount, { metamaskAddress, tronAddress }, - combinedReceiver + combinedReceiver, + this.simulateAmount ); } diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index 5486c41ea..3460b5946 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -21,15 +21,134 @@ import { UniversalSwapHandler, checkEvmAddress, calculateMinimum } from 'pages/U import { Type, generateContractMessages, simulateSwap } from 'rest/api'; import * as restApi from 'rest/api'; import { IBCInfo } from 'types/ibc'; -import { senderAddress } from './common'; +import { client, deployIcs20Token, deployToken, senderAddress } from './common'; import { CwIcs20LatestClient } from '@oraichain/common-contracts-sdk'; import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; +import { CWSimulateApp, GenericError, IbcOrder, IbcPacket } from '@oraichain/cw-simulate'; +import bech32 from 'bech32'; describe('universal-swap', () => { let windowSpy: jest.SpyInstance; - beforeAll(() => { + + let bobAddress = 'orai1ur2vsjrjarygawpdwtqteaazfchvw4fg6uql76'; + let oraiAddress = 'orai12zyu8w93h0q2lcnt50g3fn0w3yqnhy4fvawaqz'; + let routerContractAddress = 'placeholder'; + let ibcTransferAmount = '100000000'; + let initialBalanceAmount = '10000000000000'; + let channel = 'channel-29'; + let airiIbcDenom: string = 'oraib0x7e2A35C746F2f7C240B664F1Da4DD100141AE71F'; + let cosmosSenderAddress = bech32.encode('cosmos', bech32.decode(oraiAddress).words); + let ics20Contract; + let oraiPort; + beforeAll(async () => { windowSpy = jest.spyOn(window, 'window', 'get'); + + ics20Contract = await deployIcs20Token(client, { swap_router_contract: routerContractAddress }); + oraiPort = 'wasm.' + ics20Contract.contractAddress; + let cosmosPort: string = 'transfer'; + let airiToken = await deployToken(client, { + decimals: 6, + symbol: 'AIRI', + name: 'Airight token', + initial_balances: [{ address: ics20Contract.contractAddress, amount: initialBalanceAmount }] + }); + const cosmosChain = new CWSimulateApp({ + chainId: 'cosmoshub-4', + bech32Prefix: 'cosmos' + }); + + let newPacketData = { + src: { + port_id: cosmosPort, + channel_id: channel + }, + dest: { + port_id: oraiPort, + channel_id: channel + }, + sequence: 27, + timeout: { + block: { + revision: 1, + height: 12345678 + } + } + }; + newPacketData.dest.port_id = oraiPort; + + // init ibc channel between two chains + client.app.ibc.relay(channel, oraiPort, channel, cosmosPort, cosmosChain); + await cosmosChain.ibc.sendChannelOpen({ + open_init: { + channel: { + counterparty_endpoint: { + port_id: oraiPort, + channel_id: channel + }, + endpoint: { + port_id: cosmosPort, + channel_id: channel + }, + order: IbcOrder.Unordered, + version: 'ics20-1', + connection_id: 'connection-38' + } + } + }); + await cosmosChain.ibc.sendChannelConnect({ + open_ack: { + channel: { + counterparty_endpoint: { + port_id: oraiPort, + channel_id: channel + }, + endpoint: { + port_id: cosmosPort, + channel_id: channel + }, + order: IbcOrder.Unordered, + version: 'ics20-1', + connection_id: 'connection-38' + }, + counterparty_version: 'ics20-1' + } + }); + + cosmosChain.ibc.addMiddleWare((msg, app) => { + const data = msg.data.packet as IbcPacket; + if (Number(data.timeout.timestamp) < cosmosChain.time) { + throw new GenericError('timeout at ' + data.timeout.timestamp); + } + }); + + await ics20Contract.updateMappingPair({ + localAssetInfo: { + token: { + contract_addr: airiToken.contractAddress + } + }, + localAssetInfoDecimals: 6, + denom: airiIbcDenom, + remoteDecimals: 6, + localChannelId: channel + }); + + const icsPackage = { + amount: ibcTransferAmount, + denom: airiIbcDenom, + receiver: bobAddress, + sender: cosmosSenderAddress, + memo: '' + }; + await cosmosChain.ibc.sendPacketReceive({ + packet: { + data: toBinary(icsPackage), + ...newPacketData + }, + relayer: cosmosSenderAddress + }); }); + it('max amount', () => { const amount = 123456789n; const decimals = 6; @@ -950,6 +1069,52 @@ describe('universal-swap', () => { } ); + it.each([ + [ + '1000000000000000000000000000000000000000', + flattenTokens.find((t) => t.chainId === 'oraibridge-subnet-2' && t.coinGeckoId === 'airight'), + channel, + true, + { + ex: { + message: `${channel}/${airiIbcDenom} is not enough balance!` + } + } + ], + [ + '100000', + flattenTokens.find((t) => t.chainId === 'oraibridge-subnet-2' && t.coinGeckoId === 'airight'), + channel, + false, + { + ex: { + message: `${channel}/${airiIbcDenom} is not enough balance!` + } + } + ] + ])( + 'test-universal-swap-check-balance-channel-ibc-%', + async ( + amount: string, + toToken: TokenItemType, + channel: string, + expectedBalance: boolean, + expectedError: object + ) => { + const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], toToken, 1, amount, 1, ics20Contract); + try { + const balance = await universalSwap.checkBalanceChannelIbc({ + source: oraiPort, + channel: channel, + timeout: 3600 + }); + expect(balance).toEqual(expectedBalance); + } catch (error) { + expect(error).toEqual(expectedError); + } + } + ); + describe('checkEvmAddress', () => { const testCases = [ ['0x01', '', undefined], From ac0b69d81b07ceda5c1f1af90e3e53d525acdc7e Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Wed, 23 Aug 2023 14:13:37 +0700 Subject: [PATCH 05/11] update logic check balance evm --- src/pages/Balance/helpers.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index a55aed849..15a8957a7 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -284,7 +284,10 @@ const checkBalanceIBCOraichain = async ( const { balance } = await getBalanceIBCOraichain(token); if (token.coinGeckoId === to.coinGeckoId && balance < toDisplay(amount.toAmount, to.decimals)) { throw generateError( - `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.toAmount}, have ${balance}` + `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${toDisplay( + amount.toAmount, + to.decimals + )}, have ${balance}` ); } if (token.coinGeckoId === from.coinGeckoId && balance < amount.fromAmount) { @@ -318,11 +321,10 @@ export const transferEvmToIBC = async ( } // check balance ibc wasm - simulateAmount && - (await checkBalanceIBCOraichain(info.to, info.from, { - fromAmount, - toAmount: simulateAmount - })); + await checkBalanceIBCOraichain(info.to, info.from, { + fromAmount, + toAmount: simulateAmount + }); await window.Metamask.checkOrIncreaseAllowance(info.from, finalTransferAddress, gravityContractAddr, fromAmount); const result = await window.Metamask.transferToGravity( From 5637360c578d0c2fb58e29ed8991b85675c85a4c Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Wed, 23 Aug 2023 16:00:44 +0700 Subject: [PATCH 06/11] add testcase check balance ibc wasm account --- src/pages/Balance/helpers.ts | 21 ++++++++++----------- src/tests/universal-swap.spec.ts | 26 +++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index 15a8957a7..f6aa057b0 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -241,19 +241,18 @@ export const convertTransferIBCErc20Kwt = async ( return result; }; -const getBalanceIBCOraichain = async (token: TokenItemType, tokenQueryClient?: OraiswapTokenReadOnlyInterface) => { +export const getBalanceIBCOraichain = async ( + token: TokenItemType, + tokenQueryClient?: OraiswapTokenReadOnlyInterface +) => { if (!token) return { balance: 0 }; - try { - if (token.contractAddress) { - const cw20Token = tokenQueryClient ?? new OraiswapTokenQueryClient(window.client, token.contractAddress); - const { balance } = await cw20Token.balance({ address: process.env.REACT_APP_IBC_WASM_CONTRACT }); - return { balance: toDisplay(balance, token.decimals) }; - } - const { amount } = await window.client.getBalance(process.env.REACT_APP_IBC_WASM_CONTRACT, token.denom); - return { balance: toDisplay(amount, token.decimals) }; - } catch (error) { - console.log('error: ', error); + if (token.contractAddress) { + const cw20Token = tokenQueryClient ?? new OraiswapTokenQueryClient(window.client, token.contractAddress); + const { balance } = await cw20Token.balance({ address: process.env.REACT_APP_IBC_WASM_CONTRACT }); + return { balance: toDisplay(balance, token.decimals) }; } + const { amount } = await window.client.getBalance(process.env.REACT_APP_IBC_WASM_CONTRACT, token.denom); + return { balance: toDisplay(amount, token.decimals) }; }; const checkBalanceIBCOraichain = async ( diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index 3460b5946..b62822c02 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -15,7 +15,7 @@ import { network } from 'config/networks'; import { feeEstimate } from 'helper'; import { toAmount, toDisplay, toTokenInfo } from 'libs/utils'; import Long from 'long'; -import { combineReceiver, findToToken, getDestination } from 'pages/Balance/helpers'; +import { combineReceiver, findToToken, getBalanceIBCOraichain, getDestination } from 'pages/Balance/helpers'; import { calculateMinReceive } from 'pages/SwapV2/helpers'; import { UniversalSwapHandler, checkEvmAddress, calculateMinimum } from 'pages/UniversalSwap/helpers'; import { Type, generateContractMessages, simulateSwap } from 'rest/api'; @@ -26,6 +26,7 @@ import { CwIcs20LatestClient } from '@oraichain/common-contracts-sdk'; import { DirectSecp256k1HdWallet } from '@cosmjs/proto-signing'; import { CWSimulateApp, GenericError, IbcOrder, IbcPacket } from '@oraichain/cw-simulate'; import bech32 from 'bech32'; +import { OraiswapTokenQueryClient } from '@oraichain/oraidex-contracts-sdk'; describe('universal-swap', () => { let windowSpy: jest.SpyInstance; @@ -40,17 +41,22 @@ describe('universal-swap', () => { let cosmosSenderAddress = bech32.encode('cosmos', bech32.decode(oraiAddress).words); let ics20Contract; let oraiPort; + let airiToken; + beforeAll(async () => { windowSpy = jest.spyOn(window, 'window', 'get'); ics20Contract = await deployIcs20Token(client, { swap_router_contract: routerContractAddress }); oraiPort = 'wasm.' + ics20Contract.contractAddress; let cosmosPort: string = 'transfer'; - let airiToken = await deployToken(client, { + airiToken = await deployToken(client, { decimals: 6, symbol: 'AIRI', name: 'Airight token', - initial_balances: [{ address: ics20Contract.contractAddress, amount: initialBalanceAmount }] + initial_balances: [ + { address: ics20Contract.contractAddress, amount: initialBalanceAmount }, + { address: process.env.REACT_APP_IBC_WASM_CONTRACT, amount: initialBalanceAmount } + ] }); const cosmosChain = new CWSimulateApp({ chainId: 'cosmoshub-4', @@ -1115,6 +1121,20 @@ describe('universal-swap', () => { } ); + it.each([ + [oraichainTokens.find((t) => t.coinGeckoId === 'airight'), 10000000], + [oraichainTokens.find((t) => t.coinGeckoId === 'oraichain-token'), 0] + ])('test-universal-swap-check-balance-channel-ibc-%', async (token: TokenItemType, expectedBalance: number) => { + windowSpy.mockImplementation(() => ({ + client: client + })); + const { balance } = await getBalanceIBCOraichain( + token, + new OraiswapTokenQueryClient(client, airiToken.contractAddress) + ); + expect(balance).toEqual(expectedBalance); + }); + describe('checkEvmAddress', () => { const testCases = [ ['0x01', '', undefined], From c32b0a25ee62e9b0ef12fcd16d23521a3a71c6f4 Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Thu, 24 Aug 2023 15:39:18 +0700 Subject: [PATCH 07/11] renamed vars of universal swap handler & fixed check balance --- src/pages/Balance/helpers.ts | 6 ++-- src/pages/UniversalSwap/helpers.ts | 55 ++++++++++++++++-------------- src/tests/universal-swap.spec.ts | 47 +++++++++++++++---------- 3 files changed, 62 insertions(+), 46 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index f6aa057b0..79b1428d4 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -276,9 +276,9 @@ const checkBalanceIBCOraichain = async ( } return balance; } - // ORAI ( ETH ) -> check ORAI (ORAICHAIN) (fromAmount) -> check AIRI (ORAICHAIN) (toAmount) -> AIRI (BSC) - // ORAI ( ETH ) -> check ORAI (ORAICHAIN) (fromAmount) -> check wTRX (ORAICHAIN) (toAmount) -> wTRX (TRON) - tokens = oraichainTokens.filter((t) => t.coinGeckoId === to.coinGeckoId || t.coinGeckoId === from.coinGeckoId); + // ORAI ( ETH ) -> check ORAI (ORAICHAIN - compare from amount with cw20 / native amount) (fromAmount) -> check AIRI - compare to amount with channel balance (ORAICHAIN) (toAmount) -> AIRI (BSC) + // ORAI ( ETH ) -> check ORAI (ORAICHAIN) - compare from amount with cw20 / native amount) (fromAmount) -> check wTRX - compare to amount with channel balance (ORAICHAIN) (toAmount) -> wTRX (TRON) + tokens = oraichainTokens.filter((t) => t.coinGeckoId === from.coinGeckoId); for (const token of tokens) { const { balance } = await getBalanceIBCOraichain(token); if (token.coinGeckoId === to.coinGeckoId && balance < toDisplay(amount.toAmount, to.decimals)) { diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 1c77cd34e..2758d85c8 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -74,8 +74,8 @@ export class UniversalSwapHandler { public toTokenInOrai: TokenItemType; constructor( public sender: string, - public fromToken: TokenItemType, - public toToken: TokenItemType, + public originalFromToken: TokenItemType, + public originalToToken: TokenItemType, public fromAmount: number, public simulateAmount: string, public userSlippage: number, @@ -100,8 +100,8 @@ export class UniversalSwapHandler { * @returns combined messages */ async combineMsgCosmos(): Promise { - const ibcInfo: IBCInfo = ibcInfos[this.fromToken.chainId][this.toToken.chainId]; - const toAddress = await window.Keplr.getKeplrAddr(this.toToken.chainId); + const ibcInfo: IBCInfo = ibcInfos[this.originalFromToken.chainId][this.originalToToken.chainId]; + const toAddress = await window.Keplr.getKeplrAddr(this.originalToToken.chainId); if (!toAddress) throw generateError('Please login keplr!'); const amount = coin(this.simulateAmount, this.toTokenInOrai.denom); @@ -119,7 +119,7 @@ export class UniversalSwapHandler { }; // if not same coingeckoId, swap first then transfer token that have same coingeckoid. - if (this.fromToken.coinGeckoId !== this.toToken.coinGeckoId) { + if (this.originalFromToken.coinGeckoId !== this.originalToToken.coinGeckoId) { const msgSwap = this.generateMsgsSwap(); const msgExecuteSwap = getExecuteContractMsgs(this.sender, parseExecuteContractMultiple(msgSwap)); return [...msgExecuteSwap, msgTransfer]; @@ -130,7 +130,7 @@ export class UniversalSwapHandler { getTranferAddress(metamaskAddress: string, tronAddress: string, ibcInfo: IBCInfo) { let transferAddress = metamaskAddress; // check tron network and convert address - if (this.toToken.prefix === ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX) { + if (this.originalToToken.prefix === ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX) { transferAddress = tronToEthAddress(tronAddress); } // only allow transferring back to ethereum / bsc only if there's metamask address and when the metamask address is used, which is in the ibcMemo variable @@ -141,7 +141,7 @@ export class UniversalSwapHandler { } getIbcMemo(transferAddress: string) { - return this.toToken.chainId === 'oraibridge-subnet-2' ? this.toToken.prefix + transferAddress : ''; + return this.originalToToken.chainId === 'oraibridge-subnet-2' ? this.originalToToken.prefix + transferAddress : ''; } /** @@ -151,15 +151,15 @@ export class UniversalSwapHandler { async combineMsgEvm(metamaskAddress: string, tronAddress: string) { let msgExecuteSwap: EncodeObject[] = []; // if from and to dont't have same coingeckoId, create swap msg to combine with bridge msg - if (this.fromToken.coinGeckoId !== this.toToken.coinGeckoId) { + if (this.originalFromToken.coinGeckoId !== this.originalToToken.coinGeckoId) { const msgSwap = this.generateMsgsSwap(); msgExecuteSwap = getExecuteContractMsgs(this.sender, parseExecuteContractMultiple(msgSwap)); } // then find new _toToken in Oraibridge that have same coingeckoId with originalToToken. - this.toToken = findToToken(this.toTokenInOrai, this.toToken.chainId); + this.originalToToken = findToToken(this.toTokenInOrai, this.originalToToken.chainId); - const toAddress = await window.Keplr.getKeplrAddr(this.toToken.chainId); + const toAddress = await window.Keplr.getKeplrAddr(this.originalToToken.chainId); if (!toAddress) throw generateError('Please login keplr!'); const { ibcInfo, ibcMemo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); @@ -171,8 +171,8 @@ export class UniversalSwapHandler { } getIbcInfoIbcMemo(metamaskAddress: string, tronAddress: string) { - if (!ibcInfos[this.fromToken.chainId]) throw generateError('Cannot find ibc info'); - const ibcInfo: IBCInfo = ibcInfos[this.fromToken.chainId][this.toToken.chainId]; + if (!ibcInfos[this.originalFromToken.chainId]) throw generateError('Cannot find ibc info'); + const ibcInfo: IBCInfo = ibcInfos[this.originalFromToken.chainId][this.originalToToken.chainId]; const transferAddress = this.getTranferAddress(metamaskAddress, tronAddress, ibcInfo); const ibcMemo = this.getIbcMemo(transferAddress); return { @@ -192,13 +192,13 @@ export class UniversalSwapHandler { for (let balance of balances) { if ( 'native' in balance && - balance.native.denom === `${ibcInfo.source}/${ibcInfo.channel}/${this.toToken.denom}` + balance.native.denom === `${ibcInfo.source}/${ibcInfo.channel}/${this.originalToToken.denom}` ) { const pairMapping = await ics20Contract.pairMapping({ key: balance.native.denom }); const trueBalance = toDisplay(balance.native.amount, pairMapping.pair_mapping.remote_decimals); const _toAmount = toDisplay(this.simulateAmount); if (trueBalance < _toAmount) { - throw generateError(`${ibcInfo.channel}/${this.toToken.denom} is not enough balance!`); + throw generateError(`${ibcInfo.channel}/${this.originalToToken.denom} is not enough balance!`); } } else { // do nothing because currently we dont have any cw20 balance in the channel @@ -219,26 +219,27 @@ export class UniversalSwapHandler { } async combineMsgs(metamaskAddress: string, tronAddress: string): Promise { - if (this.toToken.chainId === 'cosmoshub-4' || this.toToken.chainId === 'osmosis-1') return this.combineMsgCosmos(); + if (this.originalToToken.chainId === 'cosmoshub-4' || this.originalToToken.chainId === 'osmosis-1') + return this.combineMsgCosmos(); return this.combineMsgEvm(metamaskAddress, tronAddress); } // Universal swap from Oraichain to cosmos-hub | osmosis | EVM networks. async swapAndTransfer({ metamaskAddress, tronAddress }: SwapData): Promise { - // find to token in Oraichain to swap first and use this.toTokenInOrai as fromToken in bridge message. - this.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === this.toToken.coinGeckoId); + // find to token in Oraichain to swap first and use this.toTokenInOrai as originalFromToken in bridge message. + this.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === this.originalToToken.coinGeckoId); const combinedMsgs = await this.combineMsgs(metamaskAddress, tronAddress); const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); await this.checkBalanceChannelIbc(ibcInfo); // handle sign and broadcast transactions - const offlineSigner = await window.Keplr.getOfflineSigner(this.fromToken.chainId); + const offlineSigner = await window.Keplr.getOfflineSigner(this.originalFromToken.chainId); const aminoTypes = new AminoTypes({ ...createWasmAminoConverters(), ...customAminoTypes }); - const client = await SigningStargateClient.connectWithSigner(this.fromToken.rpc, offlineSigner, { + const client = await SigningStargateClient.connectWithSigner(this.originalFromToken.rpc, offlineSigner, { registry: customRegistry, aminoTypes, gasPrice: GasPrice.fromString(`${await getNetworkGasPrice()}${network.denom}`) @@ -249,7 +250,7 @@ export class UniversalSwapHandler { async transferAndSwap(combinedReceiver: string, metamaskAddress?: string, tronAddress?: string): Promise { return transferEvmToIBC( - { from: this.fromToken, to: this.toToken }, + { from: this.originalFromToken, to: this.originalToToken }, this.fromAmount, { metamaskAddress, tronAddress }, combinedReceiver, @@ -265,15 +266,19 @@ export class UniversalSwapHandler { generateMsgsSwap() { try { - const _fromAmount = toAmount(this.fromAmount, this.fromToken.decimals).toString(); + const _fromAmount = toAmount(this.fromAmount, this.originalFromToken.decimals).toString(); - const minimumReceive = this.calculateMinReceive(this.simulateAmount, this.userSlippage, this.fromToken.decimals); + const minimumReceive = this.calculateMinReceive( + this.simulateAmount, + this.userSlippage, + this.originalFromToken.decimals + ); const msgs = generateContractMessages({ type: Type.SWAP, sender: this.sender, amount: _fromAmount, - fromInfo: this.fromToken!, - toInfo: this.toTokenInOrai ?? this.toToken, + fromInfo: this.originalFromToken!, + toInfo: this.toTokenInOrai ?? this.originalToToken, minimumReceive } as SwapQuery); const msg = msgs[0]; @@ -304,7 +309,7 @@ export class UniversalSwapHandler { const msg: TransferBackMsg = { local_channel_id: ibcInfo.channel, remote_address: toAddress, - remote_denom: this.toToken.denom, + remote_denom: this.originalToToken.denom, timeout: ibcInfo.timeout, memo: ibcMemo }; diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index b62822c02..af9b20162 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -520,8 +520,8 @@ describe('universal-swap', () => { it('test-universal-swap-constructor', async () => { const universalSwap = new UniversalSwapHandler('foo', oraichainTokens[0], oraichainTokens[0], 1, '1', 1); expect(universalSwap.sender).toEqual('foo'); - expect(universalSwap.fromToken).toBeDefined(); - expect(universalSwap.toToken).toBeDefined(); + expect(universalSwap.originalFromToken).toBeDefined(); + expect(universalSwap.originalToToken).toBeDefined(); expect(universalSwap.fromAmount).toEqual(1); expect(universalSwap.simulateAmount).toEqual('1'); expect(universalSwap.userSlippage).toEqual(1); @@ -564,8 +564,8 @@ describe('universal-swap', () => { transferAndSwapSpy.mockResolvedValue('transferAndSwap'); const fromToken = flattenTokens.find((item) => item.coinGeckoId === 'airight' && item.chainId === '0x38'); const toToken = flattenTokens.find((item) => item.coinGeckoId === 'tether' && item.chainId === '0x2b6653dc'); - universalSwap.fromToken = fromToken; - universalSwap.toToken = toToken; + universalSwap.originalFromToken = fromToken; + universalSwap.originalFromToken = toToken; const result = await universalSwap.processUniversalSwap('', universalSwapType, {}); expect(result).toEqual(expectedFunction); }); @@ -578,8 +578,8 @@ describe('universal-swap', () => { ])('test-combine-msgs-logic', async (chainId, expectedMsgType) => { const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); const toToken = flattenTokens.find((item) => item.coinGeckoId === 'tether'); - universalSwap.toToken = toToken; - universalSwap.toToken.chainId = chainId; + universalSwap.originalToToken = toToken; + universalSwap.originalToToken.chainId = chainId; universalSwap.combineMsgCosmos = async () => { return new Promise((resolve) => resolve([{ value: 'any', typeUrl: 'cosmos' }])); }; @@ -656,8 +656,10 @@ describe('universal-swap', () => { expectedSwapContractAddr: string, expectedFunds: any ) => { - universalSwap.fromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoinGeckoId); - universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoinGeckoId && t.chainId === toChainId); + universalSwap.originalFromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoinGeckoId); + universalSwap.originalToToken = flattenTokens.find( + (t) => t.coinGeckoId === toCoinGeckoId && t.chainId === toChainId + ); const swapMsg = universalSwap.generateMsgsSwap(); expect(swapMsg[0].contractAddress).toEqual(expectedSwapContractAddr); expect(swapMsg[0].handleMsg.toString()).toEqual(JSON.stringify(expectedSwapMsg)); @@ -753,10 +755,13 @@ describe('universal-swap', () => { expectedContractAddr, expectedFunds ) => { - universalSwap.fromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoingeckoId); - universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId); + universalSwap.originalFromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoingeckoId); + universalSwap.originalToToken = flattenTokens.find( + (t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId + ); universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); - const ibcInfo: IBCInfo = ibcInfos[universalSwap.fromToken.chainId][universalSwap.toToken.chainId]; + const ibcInfo: IBCInfo = + ibcInfos[universalSwap.originalFromToken.chainId][universalSwap.originalToToken.chainId]; const toAddress = 'foobar'; const ibcMemo = ''; const msg = universalSwap.generateMsgsTransferOraiToEvm(ibcInfo, toAddress, ibcMemo); @@ -863,8 +868,10 @@ describe('universal-swap', () => { } } })); - universalSwap.fromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoingeckoId); - universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId); + universalSwap.originalFromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoingeckoId); + universalSwap.originalToToken = flattenTokens.find( + (t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId + ); universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); const msg = await universalSwap.combineMsgCosmos(); expect(msg).toEqual(expectedTransferMsg); @@ -974,8 +981,10 @@ describe('universal-swap', () => { } } })); - universalSwap.fromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoingeckoId); - universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId); + universalSwap.originalFromToken = oraichainTokens.find((t) => t.coinGeckoId === fromCoingeckoId); + universalSwap.originalToToken = flattenTokens.find( + (t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId + ); universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); const msg = await universalSwap.combineMsgEvm('0x1234', 'T1234'); @@ -1013,7 +1022,9 @@ describe('universal-swap', () => { } ] ])('test-get-ibc-info-ibc-memo', (_name: string, toCoingeckoId, toChainId, expectedTransferMsg) => { - universalSwap.toToken = flattenTokens.find((t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId); + universalSwap.originalToToken = flattenTokens.find( + (t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId + ); universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); const msg = universalSwap.getIbcInfoIbcMemo('0x1234', 'T1234'); expect(msg).toEqual(expectedTransferMsg); @@ -1052,7 +1063,7 @@ describe('universal-swap', () => { const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); const ibcInfo = ibcInfos['Oraichain']['oraibridge-subnet-2']; universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === 'airight'); - universalSwap.toToken = toToken; + universalSwap.originalToToken = toToken; try { const transferAddress = universalSwap.getTranferAddress(metamaskAddress, tronAddress, ibcInfo); expect(transferAddress).toEqual(expectedTransferAddr); @@ -1069,7 +1080,7 @@ describe('universal-swap', () => { 'test getIbcMemo should return ibc memo correctly', (transferAddress: string, toToken: TokenItemType, expectedIbcMemo: string) => { const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], oraichainTokens[0], 1, '', 1); - universalSwap.toToken = toToken; + universalSwap.originalToToken = toToken; const ibcMemo = universalSwap.getIbcMemo(transferAddress); expect(ibcMemo).toEqual(expectedIbcMemo); } From ab4218ee4f38226feb91c3667f3808b5d2c6f69f Mon Sep 17 00:00:00 2001 From: ducphamle2 Date: Thu, 24 Aug 2023 16:50:33 +0700 Subject: [PATCH 08/11] updated check balance flow & updated test cases --- src/pages/Balance/helpers.ts | 48 -------------------- src/pages/UniversalSwap/helpers.ts | 71 ++++++++++++++++++++++++------ src/tests/universal-swap.spec.ts | 61 ++++++++----------------- 3 files changed, 76 insertions(+), 104 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index 79b1428d4..a21ad2d6a 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -255,48 +255,6 @@ export const getBalanceIBCOraichain = async ( return { balance: toDisplay(amount, token.decimals) }; }; -const checkBalanceIBCOraichain = async ( - to: TokenItemType, - from: TokenItemType, - amount: { - toAmount: string; - fromAmount: number; - } -) => { - let tokens; - // ORAI ( ETH ) -> check ORAI (ORAICHAIN) -> ORAI (BSC) - if (to.coinGeckoId === from.coinGeckoId) { - tokens = oraichainTokens.find((t) => t.coinGeckoId === from.coinGeckoId); - if (!tokens) return 0; - const { balance } = await getBalanceIBCOraichain(tokens); - if (balance < amount.fromAmount) { - throw generateError( - `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.fromAmount}, have ${balance}` - ); - } - return balance; - } - // ORAI ( ETH ) -> check ORAI (ORAICHAIN - compare from amount with cw20 / native amount) (fromAmount) -> check AIRI - compare to amount with channel balance (ORAICHAIN) (toAmount) -> AIRI (BSC) - // ORAI ( ETH ) -> check ORAI (ORAICHAIN) - compare from amount with cw20 / native amount) (fromAmount) -> check wTRX - compare to amount with channel balance (ORAICHAIN) (toAmount) -> wTRX (TRON) - tokens = oraichainTokens.filter((t) => t.coinGeckoId === from.coinGeckoId); - for (const token of tokens) { - const { balance } = await getBalanceIBCOraichain(token); - if (token.coinGeckoId === to.coinGeckoId && balance < toDisplay(amount.toAmount, to.decimals)) { - throw generateError( - `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${toDisplay( - amount.toAmount, - to.decimals - )}, have ${balance}` - ); - } - if (token.coinGeckoId === from.coinGeckoId && balance < amount.fromAmount) { - throw generateError( - `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.fromAmount}, have ${balance}` - ); - } - } - return 0; -}; export const transferEvmToIBC = async ( info: { from: TokenItemType; @@ -319,12 +277,6 @@ export const transferEvmToIBC = async ( throw generateError('No gravity contract addr or no from token'); } - // check balance ibc wasm - await checkBalanceIBCOraichain(info.to, info.from, { - fromAmount, - toAmount: simulateAmount - }); - await window.Metamask.checkOrIncreaseAllowance(info.from, finalTransferAddress, gravityContractAddr, fromAmount); const result = await window.Metamask.transferToGravity( info.from, diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 2758d85c8..018e5ab2d 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -20,8 +20,8 @@ import CosmJs, { getExecuteContractMsgs, parseExecuteContractMultiple } from 'li import { MsgTransfer } from 'libs/proto/ibc/applications/transfer/v1/tx'; import customRegistry, { customAminoTypes } from 'libs/registry'; import { atomic, buildMultipleMessages, generateError, toAmount, toDisplay } from 'libs/utils'; -import { findToToken, transferEvmToIBC } from 'pages/Balance/helpers'; -import { SwapQuery, Type, generateContractMessages, parseTokenInfo } from 'rest/api'; +import { findToToken, getBalanceIBCOraichain, transferEvmToIBC } from 'pages/Balance/helpers'; +import { SwapQuery, Type, generateContractMessages, getTokenOnOraichain, parseTokenInfo } from 'rest/api'; import { IBCInfo } from 'types/ibc'; /** @@ -99,7 +99,7 @@ export class UniversalSwapHandler { * Combine messages for universal swap token from Oraichain to Cosmos networks(Osmosis | Cosmos-hub). * @returns combined messages */ - async combineMsgCosmos(): Promise { + async combineMsgCosmos(timeoutTimestamp?: string): Promise { const ibcInfo: IBCInfo = ibcInfos[this.originalFromToken.chainId][this.originalToToken.chainId]; const toAddress = await window.Keplr.getKeplrAddr(this.originalToToken.chainId); if (!toAddress) throw generateError('Please login keplr!'); @@ -114,7 +114,7 @@ export class UniversalSwapHandler { sender: this.sender, receiver: toAddress, memo: '', - timeoutTimestamp: calculateTimeoutTimestamp(ibcInfo.timeout) + timeoutTimestamp: timeoutTimestamp ?? calculateTimeoutTimestamp(ibcInfo.timeout) }) }; @@ -181,7 +181,11 @@ export class UniversalSwapHandler { }; } - async checkBalanceChannelIbc(ibcInfo: IBCInfo) { + buildIbcWasmPairKey(ibcPort: string, ibcChannel: string, denom: string) { + return `${ibcPort}/${ibcChannel}/${denom}`; + } + + async checkBalanceChannelIbc(ibcInfo: IBCInfo, toToken: TokenItemType) { const ics20Contract = this.cwIcs20LatestClient ?? new CwIcs20LatestQueryClient(window.client, process.env.REACT_APP_IBC_WASM_CONTRACT); const { balances } = await ics20Contract.channel({ @@ -190,21 +194,56 @@ export class UniversalSwapHandler { }); for (let balance of balances) { - if ( - 'native' in balance && - balance.native.denom === `${ibcInfo.source}/${ibcInfo.channel}/${this.originalToToken.denom}` - ) { - const pairMapping = await ics20Contract.pairMapping({ key: balance.native.denom }); + if ('native' in balance) { + let pairKey = this.buildIbcWasmPairKey(ibcInfo.source, ibcInfo.channel, toToken.denom); + if (toToken.prefix && toToken.contractAddress) { + pairKey = this.buildIbcWasmPairKey( + ibcInfo.source, + ibcInfo.channel, + `${toToken.prefix}${toToken.contractAddress}` + ); + } + if (pairKey !== balance.native.denom) continue; + const pairMapping = await ics20Contract.pairMapping({ key: pairKey }); const trueBalance = toDisplay(balance.native.amount, pairMapping.pair_mapping.remote_decimals); - const _toAmount = toDisplay(this.simulateAmount); + const _toAmount = toDisplay(this.simulateAmount, toToken.decimals); if (trueBalance < _toAmount) { - throw generateError(`${ibcInfo.channel}/${this.originalToToken.denom} is not enough balance!`); + throw generateError(`pair key is not enough balance!`); } } else { // do nothing because currently we dont have any cw20 balance in the channel } } - return false; + } + + // ORAI ( ETH ) -> check ORAI (ORAICHAIN - compare from amount with cw20 / native amount) (fromAmount) -> check AIRI - compare to amount with channel balance (ORAICHAIN) (toAmount) -> AIRI (BSC) + // ORAI ( ETH ) -> check ORAI (ORAICHAIN) - compare from amount with cw20 / native amount) (fromAmount) -> check wTRX - compare to amount with channel balance (ORAICHAIN) (toAmount) -> wTRX (TRON) + async checkBalanceIBCOraichain( + to: TokenItemType, + from: TokenItemType, + amount: { + toAmount: string; + fromAmount: number; + } + ) { + // ORAI ( ETH ) -> check ORAI (ORAICHAIN) -> ORAI (BSC) + // no need to check this case because users will swap directly. This case should be impossible because it is only called when transferring from evm to other networks + if (from.chainId === 'Oraichain' && to.chainId === from.chainId) return; + // always check from token in ibc wasm should have enough tokens to swap / send to destination + const token = getTokenOnOraichain(from.coinGeckoId); + if (!token) return; + const { balance } = await getBalanceIBCOraichain(token); + if (balance < amount.fromAmount) { + throw generateError( + `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.fromAmount}, have ${balance}` + ); + } + // if to token is evm, then we need to evaluate channel state balance of ibc wasm + if (to.chainId === '0x01' || to.chainId === '0x38' || to.chainId === '0x2b6653dc') { + const ibcInfo: IBCInfo | undefined = ibcInfos[getTokenOnOraichain(from.coinGeckoId)?.chainId][to.chainId]; + if (!ibcInfo) throw generateError('IBC Info error when checking ibc balance'); + await this.checkBalanceChannelIbc(ibcInfo, this.originalToToken); + } } async swap(): Promise { @@ -231,7 +270,7 @@ export class UniversalSwapHandler { const combinedMsgs = await this.combineMsgs(metamaskAddress, tronAddress); const { ibcInfo } = this.getIbcInfoIbcMemo(metamaskAddress, tronAddress); - await this.checkBalanceChannelIbc(ibcInfo); + await this.checkBalanceChannelIbc(ibcInfo, this.originalToToken); // handle sign and broadcast transactions const offlineSigner = await window.Keplr.getOfflineSigner(this.originalFromToken.chainId); @@ -249,6 +288,10 @@ export class UniversalSwapHandler { } async transferAndSwap(combinedReceiver: string, metamaskAddress?: string, tronAddress?: string): Promise { + await this.checkBalanceIBCOraichain(this.originalToToken, this.originalFromToken, { + fromAmount: this.fromAmount, + toAmount: this.simulateAmount + }); return transferEvmToIBC( { from: this.originalFromToken, to: this.originalToToken }, this.fromAmount, diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index af9b20162..993039aba 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -786,9 +786,7 @@ describe('universal-swap', () => { token: { denom: process.env.REACT_APP_OSMOSIS_ORAICHAIN_DENOM, amount: simulateAmount }, //osmosis denom sender: senderAddress, receiver: 'orai1234', - timeoutTimestamp: Long.fromNumber(Math.floor(Date.now() / 1000) + IBC_TRANSFER_TIMEOUT) - .multiply(1000000000) - .toString(), + timeoutTimestamp: '0', memo: '' } } @@ -849,10 +847,7 @@ describe('universal-swap', () => { token: { denom: process.env.REACT_APP_OSMOSIS_ORAICHAIN_DENOM, amount: simulateAmount }, sender: senderAddress, receiver: 'orai1234', - timeoutTimestamp: Long.fromNumber(Math.floor(Date.now() / 1000) + IBC_TRANSFER_TIMEOUT) - .multiply(1000000000) - .toString(), - timeoutHeight: undefined, + timeoutTimestamp: '0', memo: '' } } @@ -873,7 +868,7 @@ describe('universal-swap', () => { (t) => t.coinGeckoId === toCoingeckoId && t.chainId === toChainId ); universalSwap.toTokenInOrai = oraichainTokens.find((t) => t.coinGeckoId === toCoingeckoId); - const msg = await universalSwap.combineMsgCosmos(); + const msg = await universalSwap.combineMsgCosmos('0'); expect(msg).toEqual(expectedTransferMsg); } ); @@ -1089,45 +1084,27 @@ describe('universal-swap', () => { it.each([ [ '1000000000000000000000000000000000000000', - flattenTokens.find((t) => t.chainId === 'oraibridge-subnet-2' && t.coinGeckoId === 'airight'), + flattenTokens.find((t) => t.chainId === '0x38' && t.coinGeckoId === 'airight'), channel, - true, - { - ex: { - message: `${channel}/${airiIbcDenom} is not enough balance!` - } - } + true ], - [ - '100000', - flattenTokens.find((t) => t.chainId === 'oraibridge-subnet-2' && t.coinGeckoId === 'airight'), - channel, - false, - { - ex: { - message: `${channel}/${airiIbcDenom} is not enough balance!` - } - } - ] + ['10', flattenTokens.find((t) => t.chainId === '0x38' && t.coinGeckoId === 'airight'), channel, false] ])( - 'test-universal-swap-check-balance-channel-ibc-%', - async ( - amount: string, - toToken: TokenItemType, - channel: string, - expectedBalance: boolean, - expectedError: object - ) => { + 'test-universal-swap-checkBalanceChannelIbc-%', + async (amount: string, toToken: TokenItemType, channel: string, willThrow: boolean) => { const universalSwap = new UniversalSwapHandler('', oraichainTokens[0], toToken, 1, amount, 1, ics20Contract); try { - const balance = await universalSwap.checkBalanceChannelIbc({ - source: oraiPort, - channel: channel, - timeout: 3600 - }); - expect(balance).toEqual(expectedBalance); + await universalSwap.checkBalanceChannelIbc( + { + source: oraiPort, + channel: channel, + timeout: 3600 + }, + toToken + ); + expect(willThrow).toEqual(false); } catch (error) { - expect(error).toEqual(expectedError); + expect(willThrow).toEqual(true); } } ); @@ -1135,7 +1112,7 @@ describe('universal-swap', () => { it.each([ [oraichainTokens.find((t) => t.coinGeckoId === 'airight'), 10000000], [oraichainTokens.find((t) => t.coinGeckoId === 'oraichain-token'), 0] - ])('test-universal-swap-check-balance-channel-ibc-%', async (token: TokenItemType, expectedBalance: number) => { + ])('test-universal-swap-getBalanceIBCOraichain-ibc-%', async (token: TokenItemType, expectedBalance: number) => { windowSpy.mockImplementation(() => ({ client: client })); From 6dd0da24ceb191f1f68e7a6c6db2ba68829d2116 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Fri, 25 Aug 2023 09:32:48 +0700 Subject: [PATCH 09/11] unit test func checkBalanceIBCOraichain --- src/pages/UniversalSwap/helpers.ts | 8 +++-- src/tests/universal-swap.spec.ts | 48 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 018e5ab2d..cb68e4945 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -23,6 +23,7 @@ import { atomic, buildMultipleMessages, generateError, toAmount, toDisplay } fro import { findToToken, getBalanceIBCOraichain, transferEvmToIBC } from 'pages/Balance/helpers'; import { SwapQuery, Type, generateContractMessages, getTokenOnOraichain, parseTokenInfo } from 'rest/api'; import { IBCInfo } from 'types/ibc'; +import { OraiswapTokenReadOnlyInterface } from '@oraichain/oraidex-contracts-sdk'; /** * Get transfer token fee when universal swap @@ -224,7 +225,8 @@ export class UniversalSwapHandler { amount: { toAmount: string; fromAmount: number; - } + }, + tokenQueryClient?: OraiswapTokenReadOnlyInterface ) { // ORAI ( ETH ) -> check ORAI (ORAICHAIN) -> ORAI (BSC) // no need to check this case because users will swap directly. This case should be impossible because it is only called when transferring from evm to other networks @@ -232,7 +234,7 @@ export class UniversalSwapHandler { // always check from token in ibc wasm should have enough tokens to swap / send to destination const token = getTokenOnOraichain(from.coinGeckoId); if (!token) return; - const { balance } = await getBalanceIBCOraichain(token); + const { balance } = await getBalanceIBCOraichain(token, tokenQueryClient); if (balance < amount.fromAmount) { throw generateError( `The bridge contract does not have enough balance to process this bridge transaction. Wanted ${amount.fromAmount}, have ${balance}` @@ -240,7 +242,7 @@ export class UniversalSwapHandler { } // if to token is evm, then we need to evaluate channel state balance of ibc wasm if (to.chainId === '0x01' || to.chainId === '0x38' || to.chainId === '0x2b6653dc') { - const ibcInfo: IBCInfo | undefined = ibcInfos[getTokenOnOraichain(from.coinGeckoId)?.chainId][to.chainId]; + const ibcInfo: IBCInfo | undefined = ibcInfos['Oraichain'][to.chainId]; if (!ibcInfo) throw generateError('IBC Info error when checking ibc balance'); await this.checkBalanceChannelIbc(ibcInfo, this.originalToToken); } diff --git a/src/tests/universal-swap.spec.ts b/src/tests/universal-swap.spec.ts index 993039aba..622358bd9 100644 --- a/src/tests/universal-swap.spec.ts +++ b/src/tests/universal-swap.spec.ts @@ -1114,7 +1114,7 @@ describe('universal-swap', () => { [oraichainTokens.find((t) => t.coinGeckoId === 'oraichain-token'), 0] ])('test-universal-swap-getBalanceIBCOraichain-ibc-%', async (token: TokenItemType, expectedBalance: number) => { windowSpy.mockImplementation(() => ({ - client: client + client })); const { balance } = await getBalanceIBCOraichain( token, @@ -1123,6 +1123,52 @@ describe('universal-swap', () => { expect(balance).toEqual(expectedBalance); }); + it.each([ + [ + oraichainTokens.find((t) => t.coinGeckoId === 'oraichain-token'), // ORAI (ORAICHAIN) + oraichainTokens.find((t) => t.coinGeckoId === 'airight'), // AIRIGHT (ORAICHAIN) + 0, + '0', + false + ], + [ + flattenTokens.find((t) => t.coinGeckoId === 'oraichain-token' && t.chainId === '0x01'), // ORAI (ETH) + flattenTokens.find((t) => t.coinGeckoId === 'oraichain-token' && t.chainId === '0x38'), // ORAI (BSC) + 10000000, + '10000000', + true + ], + [ + flattenTokens.find((t) => t.coinGeckoId === 'oraichain-token' && t.chainId === '0x01'), // ORAI (ETH) + flattenTokens.find((t) => t.coinGeckoId === 'airight' && t.chainId === '0x38'), // AIRIGHT (BSC) + 10000000, + '10000000', + false + ] + ])( + 'test-universal-swap-checkBalanceIBCOraichain-ibc-%', + async (from: TokenItemType, to: TokenItemType, fromAmount: number, toAmount: string, willThrow: boolean) => { + windowSpy.mockImplementation(() => ({ + client + })); + const universalSwap = new UniversalSwapHandler('', from, to, fromAmount, toAmount, 1, ics20Contract); + try { + await universalSwap.checkBalanceIBCOraichain( + from, + to, + { + toAmount: toAmount, + fromAmount: fromAmount + }, + new OraiswapTokenQueryClient(client, airiToken.contractAddress) + ); + expect(willThrow).toEqual(false); + } catch (error) { + expect(willThrow).toEqual(true); + } + } + ); + describe('checkEvmAddress', () => { const testCases = [ ['0x01', '', undefined], From e59efc086e83ce5dd0d930156cc54f1bc0d220f0 Mon Sep 17 00:00:00 2001 From: Hau Nguyen Van Date: Thu, 31 Aug 2023 17:35:02 +0700 Subject: [PATCH 10/11] remove params not used transfer evm to ibc --- src/pages/Balance/helpers.ts | 22 +++++++--------------- src/pages/Balance/index.tsx | 2 +- src/pages/UniversalSwap/helpers.ts | 2 +- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/pages/Balance/helpers.ts b/src/pages/Balance/helpers.ts index 44edc42bb..b421f2133 100644 --- a/src/pages/Balance/helpers.ts +++ b/src/pages/Balance/helpers.ts @@ -261,10 +261,7 @@ export const getBalanceIBCOraichain = async ( }; export const transferEvmToIBC = async ( - info: { - from: TokenItemType; - to: TokenItemType; - }, + from: TokenItemType, fromAmount: number, address: { metamaskAddress?: string; @@ -274,25 +271,20 @@ export const transferEvmToIBC = async ( combinedReceiver: string ) => { const { metamaskAddress, tronAddress, oraiAddress } = address; - const finalTransferAddress = window.Metamask.getFinalEvmAddress(info.from.chainId, { + const finalTransferAddress = window.Metamask.getFinalEvmAddress(from.chainId, { metamaskAddress, tronAddress }); const oraiAddr = oraiAddress ?? (await window.Keplr.getKeplrAddr()); if (!finalTransferAddress || !oraiAddr) throw generateError('Please login both metamask or tronlink and keplr!'); - const gravityContractAddr = gravityContracts[info.from!.chainId!]; - if (!gravityContractAddr || !info.from) { + const gravityContractAddr = gravityContracts[from!.chainId!]; + if (!gravityContractAddr || !from) { throw generateError('No gravity contract addr or no from token'); } - const finalFromAmount = toAmount(fromAmount, info.from.decimals).toString(); - await window.Metamask.checkOrIncreaseAllowance(info.from, finalTransferAddress, gravityContractAddr, finalFromAmount); - const result = await window.Metamask.transferToGravity( - info.from, - finalFromAmount, - finalTransferAddress, - combinedReceiver - ); + const finalFromAmount = toAmount(fromAmount, from.decimals).toString(); + await window.Metamask.checkOrIncreaseAllowance(from, finalTransferAddress, gravityContractAddr, finalFromAmount); + const result = await window.Metamask.transferToGravity(from, finalFromAmount, finalTransferAddress, combinedReceiver); return result; }; diff --git a/src/pages/Balance/index.tsx b/src/pages/Balance/index.tsx index ae40dc072..bf0231a68 100644 --- a/src/pages/Balance/index.tsx +++ b/src/pages/Balance/index.tsx @@ -159,7 +159,7 @@ const Balance: React.FC = () => { // has to switch network to the correct chain id on evm since users can swap between network tokens await window.Metamask.switchNetwork(from.chainId); result = await transferEvmToIBC( - { from, to }, + from, fromAmount, { metamaskAddress, tronAddress, oraiAddress: latestOraiAddress }, combinedReceiver diff --git a/src/pages/UniversalSwap/helpers.ts b/src/pages/UniversalSwap/helpers.ts index 1e1c7e285..2faa556ce 100644 --- a/src/pages/UniversalSwap/helpers.ts +++ b/src/pages/UniversalSwap/helpers.ts @@ -439,7 +439,7 @@ export class UniversalSwapHandler { return window.Metamask.evmSwap(evmSwapData); } return transferEvmToIBC( - { from: this.originalFromToken, to: this.originalToToken }, + this.originalFromToken, this.fromAmount, { metamaskAddress, tronAddress }, combinedReceiver From b85d3173d8a6d8cbaff4d9ea4eaa37d596096ddd Mon Sep 17 00:00:00 2001 From: phutx Date: Thu, 31 Aug 2023 22:06:00 +0700 Subject: [PATCH 11/11] update sonarqube prokect key --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 901fcf12d..aa0c0913e 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1 +1 @@ -sonar.projectKey=oraichain_oraiswap-frontend_AYoDOebvkauZkVxG9Y0p \ No newline at end of file +sonar.projectKey=oraichain_oraiswap-frontend_AYpL50t8tGfUdSAJ_QO4 \ No newline at end of file