From b96bbec86a6b67371a63583e131cea09f925d96c Mon Sep 17 00:00:00 2001 From: vuonghuuhung Date: Fri, 27 Dec 2024 17:35:39 +0700 Subject: [PATCH 1/2] feat: add optimisticUpdateToken thunk and update gasPrice handling in cosmjs --- package.json | 3 +- src/libs/cosmjs.ts | 2 +- .../Pools/NewTokenModal/NewTokenModal.tsx | 204 +++++++++++------- src/reducer/onchainTokens.ts | 37 +++- yarn.lock | 51 ++++- 5 files changed, 211 insertions(+), 86 deletions(-) diff --git a/package.json b/package.json index 9d70589f7..200c8398d 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,13 @@ "@oraichain/ethereum-multicall": "^1.0.2", "@oraichain/kawaiiverse-txs": "^0.0.3", "@oraichain/orai-bitcoin": "2.0.0", - "@oraichain/oraidex-common": "2.0.5", "@oraichain/orai-token-inspector": "^0.1.24", + "@oraichain/oraidex-common": "2.0.5", "@oraichain/oraidex-common-ui": "1.0.11", "@oraichain/oraidex-contracts-sdk": "1.0.55", "@oraichain/oraidex-universal-swap": "1.2.0-beta28", "@oraichain/oraiswap-v3": "1.2.0-beta26", + "@oraichain/proto": "^0.0.7", "@oraichain/ton-bridge-contracts": "^0.15.8", "@oraichain/tonbridge-contracts-sdk": "^1.3.1", "@oraichain/tonbridge-sdk": "^1.3.6", diff --git a/src/libs/cosmjs.ts b/src/libs/cosmjs.ts index c3273c84d..234fb06a7 100644 --- a/src/libs/cosmjs.ts +++ b/src/libs/cosmjs.ts @@ -30,7 +30,7 @@ const getCosmWasmClient = async ( tmClient, wallet, options - ? { ...options, broadcastPollIntervalMs: 600 } + ? { ...options, gasPrice: GasPrice.fromString(network.fee.gasPrice + network.denom), broadcastPollIntervalMs: 600 } : { gasPrice: GasPrice.fromString(network.fee.gasPrice + network.denom), broadcastPollIntervalMs: 600 diff --git a/src/pages/Pools/NewTokenModal/NewTokenModal.tsx b/src/pages/Pools/NewTokenModal/NewTokenModal.tsx index 1dd34a0d0..6add7353d 100644 --- a/src/pages/Pools/NewTokenModal/NewTokenModal.tsx +++ b/src/pages/Pools/NewTokenModal/NewTokenModal.tsx @@ -1,12 +1,13 @@ import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'; -import { AccountData } from '@cosmjs/proto-signing'; +import { AccountData, Registry } from '@cosmjs/proto-signing'; import { sha256 } from '@injectivelabs/sdk-ts'; +import ArrowDownIcon from 'assets/icons/arrow.svg?react'; import PlusIcon from 'assets/icons/plus.svg?react'; import cn from 'classnames/bind'; import Input from 'components/Input'; import Loader from 'components/Loader'; import Modal from 'components/Modal'; -import { TToastType, displayToast } from 'components/Toasts/Toast'; +import { displayToast, TToastType } from 'components/Toasts/Toast'; import { getTransactionUrl, handleErrorTransaction } from 'helper'; import useConfigReducer from 'hooks/useConfigReducer'; import useOnClickOutside from 'hooks/useOnClickOutside'; @@ -14,15 +15,24 @@ import { network } from 'initCommon'; import { getCosmWasmClient } from 'libs/cosmjs'; import { validateAddressCosmos } from 'libs/utils'; import { FC, useRef, useState } from 'react'; -import { InitBalancesItems } from './ItemsComponent'; -import styles from './NewTokenModal.module.scss'; -import ArrowDownIcon from 'assets/icons/arrow.svg?react'; import NumberFormat from 'react-number-format'; -import { inspectToken } from 'reducer/onchainTokens'; import { useDispatch } from 'react-redux'; -const cx = cn.bind(styles); +import { InitBalancesItems } from './ItemsComponent'; +import styles from './NewTokenModal.module.scss'; +import * as cosmwasmTokenfactoryV1beta1TxRegistry from '@oraichain/proto/dist/codegen/cosmwasm/tokenfactory/v1beta1/tx.registry'; -const TOKEN_FACTORY_CONTRACT = 'orai1ytjgzxvtsq3ukhzmt39cp85j27zzqf5y706y9qrffrnpn3vd3uds957ydu'; +import { + MsgCreateDenom, + MsgMint, + MsgSetDenomMetadata +} from '@oraichain/proto/dist/codegen/cosmwasm/tokenfactory/v1beta1/tx'; + +import { inspectToken, optimisticUpdateToken } from 'reducer/onchainTokens'; +import { GasPrice } from '@cosmjs/stargate'; +import { OraichainNetworkConfig } from '@oraichain/orai-token-inspector'; + +const cx = cn.bind(styles); +// const TOKEN_FACTORY_CONTRACT = 'orai1ytjgzxvtsq3ukhzmt39cp85j27zzqf5y706y9qrffrnpn3vd3uds957ydu'; const DEFAULT_COSMOS_DECIMALS = 6; interface ModalProps { @@ -63,9 +73,21 @@ const NewTokenModal: FC = ({ isOpen, close, open }) => { useOnClickOutside(ref, () => handleOutsideClick()); const handleCreateToken = async () => { - const { client, defaultAddress: address } = await getCosmWasmClient({ - chainId: network.chainId - }); + const { + client, + defaultAddress: address, + wallet + } = await getCosmWasmClient( + { + chainId: network.chainId + }, + { + registry: new Registry(cosmwasmTokenfactoryV1beta1TxRegistry.registry) + } + ); + + console.log((await wallet.getAccounts())[0]); + if (!oraiAddress) return displayToast(TToastType.TX_FAILED, { message: 'Wallet address does not exist!' @@ -103,72 +125,96 @@ const NewTokenModal: FC = ({ isOpen, close, open }) => { const hash = Buffer.from(sha256(uint8Array)).toString('hex'); const symbol = tokenSymbol.trim(); - const createDenomMsg = { - contractAddress: TOKEN_FACTORY_CONTRACT, - msg: { - create_denom: { - metadata: { - base: `factory/${TOKEN_FACTORY_CONTRACT}/${tokenSymbol}`, - denom_units: [ - { - denom: `factory/${TOKEN_FACTORY_CONTRACT}/${tokenSymbol}`, - exponent: 0, - aliases: [] - }, - { - denom: tokenSymbol, - exponent: DEFAULT_COSMOS_DECIMALS, - aliases: [] - } - ], - description: description, - display: symbol, - name: tokenName.trim(), - symbol: symbol, - uri: tokenLogoUrl, - uri_hash: hash + + const createDenomMsg = MsgCreateDenom.fromPartial({ + sender: address.address, + subdenom: tokenSymbol + }); + + const setDenomMetadataMsg = MsgSetDenomMetadata.fromPartial({ + sender: address.address, + metadata: { + base: `factory/${address.address}/${tokenSymbol}`, + denomUnits: [ + { + denom: `factory/${address.address}/${tokenSymbol}`, + exponent: 0, + aliases: [] }, - subdenom: tokenSymbol - } - }, - funds: [ - { - denom: 'orai', - amount: '1' - } - ] - }; + { + denom: tokenSymbol, + exponent: DEFAULT_COSMOS_DECIMALS, + aliases: [] + } + ], + description: description, + display: symbol, + name: tokenName.trim(), + symbol: symbol, + uri: tokenLogoUrl, + uriHash: hash + } + }); - const initBalanceMsg = isInitBalances + const mintMsgs = isInitBalances ? initBalances.map((init) => { - console.log(init.amount.toString()); - return { - contractAddress: TOKEN_FACTORY_CONTRACT, - msg: { - mint_tokens: { - amount: init.amount.toString(), - denom: `factory/${TOKEN_FACTORY_CONTRACT}/${tokenSymbol}`, - mint_to_address: init.address - } - }, - funds: [] - }; + return MsgMint.fromPartial({ + sender: address.address, + mintToAddress: init.address, + amount: { + denom: `factory/${address.address}/${tokenSymbol}`, + amount: init.amount.toString() + } + }); }) : []; - msgs.push(createDenomMsg); + const mintRawMsgs = mintMsgs.map((msg) => ({ + typeUrl: '/cosmwasm.tokenfactory.v1beta1.MsgMint', + value: msg + })); - if (initBalances.length > 0) { - msgs.push(...initBalanceMsg); - } - const res = await client.executeMultiple(address.address, msgs, 'auto'); + const res = await client.signAndBroadcast( + address.address, + [ + { + typeUrl: '/cosmwasm.tokenfactory.v1beta1.MsgCreateDenom', + value: createDenomMsg + }, + { + typeUrl: '/cosmwasm.tokenfactory.v1beta1.MsgSetDenomMetadata', + value: setDenomMetadataMsg + }, + ...mintRawMsgs + ], + 'auto', + 'Create a new token' + ); if (res.transactionHash) { + // find if the initBalances has address of creator + const balance = initBalances.find((init) => init.address === address.address); + // TODO: optimistic update here dispatch( - inspectToken({ - tokenId: `factory/${TOKEN_FACTORY_CONTRACT}/${tokenSymbol}`, - address: oraiAddress, - isUserAdded: true + optimisticUpdateToken({ + token: { + denom: `factory/${address.address}/${tokenSymbol}`, + name: tokenSymbol, + decimals: DEFAULT_COSMOS_DECIMALS, + org: tokenName.trim(), + icon: tokenLogoUrl, + iconLight: tokenLogoUrl, + bridgeTo: [], + chainId: network.chainId, + cosmosBased: true, + contractAddress: '', + coinType: OraichainNetworkConfig.coinType, + description: description, + feeCurrencies: network.feeCurrencies, + gasPriceStep: network.feeCurrencies[0].gasPriceStep, + rpc: network.rpc + }, + balance: balance ? balance.amount.toString() : '0' }) ); displayToast(TToastType.TX_SUCCESSFUL, { @@ -347,18 +393,18 @@ const NewTokenModal: FC = ({ isOpen, close, open }) => { initBalances.map((item, ind) => { return ( //
- + //
); })} diff --git a/src/reducer/onchainTokens.ts b/src/reducer/onchainTokens.ts index a4c901f16..d53fce370 100644 --- a/src/reducer/onchainTokens.ts +++ b/src/reducer/onchainTokens.ts @@ -19,13 +19,20 @@ const initialState: OnchainTokensState = { export const onchainTokensSlice = createSlice({ name: 'onchainTokens', initialState, - reducers: {}, + reducers: { + + }, extraReducers: (builder) => { builder.addCase(inspectToken.fulfilled, (state, action) => { const { token } = action.payload; state.tokens = [onChainTokenToTokenItem(token)]; state.allOnChainTokens = [...state.allOnChainTokens, onChainTokenToTokenItem(token)]; }); + builder.addCase(optimisticUpdateToken.fulfilled, (state, action) => { + const { token } = action.payload; + state.tokens = [onChainTokenToTokenItem(token)]; + state.allOnChainTokens = [...state.allOnChainTokens, onChainTokenToTokenItem(token)]; + }); } }); @@ -50,6 +57,34 @@ export const onChainTokenToTokenItem = (token: InspectedToken): TokenItemType => }; }; +export const optimisticUpdateToken = createAsyncThunk( + 'onchainTokens/optimisticUpdateToken', + async ( + { + token, + balance + }: { + token: InspectedToken; + balance: string; + }, + thunkAPI + ) => { + thunkAPI.dispatch( + updateAmounts({ + [token.denom]: balance + }) + ); + + const tokenItem = onChainTokenToTokenItem(token); + thunkAPI.dispatch(updateAddedTokens([tokenItem])); + + return { + token, + balance + }; + } +) + export const inspectToken = createAsyncThunk( 'onchainTokens/inspectToken', async ( diff --git a/yarn.lock b/yarn.lock index 6809f42d9..fa147e403 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1769,6 +1769,13 @@ resolved "https://registry.yarnpkg.com/@cosmjs/utils/-/utils-0.32.4.tgz#a9a717c9fd7b1984d9cefdd0ef6c6f254060c671" integrity sha512-D1Yc+Zy8oL/hkUkFUL/bwxvuDBzRGpc4cF7/SkdhxX4iHpSLgdOuTt1mhCh9+kl6NQREy9t7SYZ6xeW5gFe60w== +"@cosmology/lcd@^0.13.3": + version "0.13.5" + resolved "https://registry.yarnpkg.com/@cosmology/lcd/-/lcd-0.13.5.tgz#1bc4d43d525ecd68d09211d2ce17332a149a1a00" + integrity sha512-CI8KFsJcgp0RINF8wHpv3Y9yR4Fb9ZnGucyoUICjtX2XT4NVBK+fvZuRFj5TP34km8TpEOb+WV2T7IN/pZsD7Q== + dependencies: + axios "1.7.4" + "@dabh/diagnostics@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.3.tgz#7f7e97ee9a725dffc7808d93668cc984e1dc477a" @@ -4069,6 +4076,17 @@ "@cosmjs/crypto" "0.32.4" "@cosmjs/proto-signing" "0.32.4" +"@oraichain/proto@^0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@oraichain/proto/-/proto-0.0.7.tgz#06b1642eea1b6df0696932dc286d37577d1a5355" + integrity sha512-kmL7Iunve5FEBKlXwlX97p0oix5dgKpLZhp0Bt6zd7CTqF3QANJUMbAPm1AAxkR4r4kkUMYarmjsDdolG6QPIQ== + dependencies: + "@cosmjs/amino" "^0.32.4" + "@cosmjs/proto-signing" "^0.32.4" + "@cosmjs/stargate" "^0.32.4" + "@cosmjs/tendermint-rpc" "^0.32.4" + "@cosmology/lcd" "^0.13.3" + "@oraichain/ton-bridge-contracts@^0.15.8": version "0.15.9" resolved "https://registry.yarnpkg.com/@oraichain/ton-bridge-contracts/-/ton-bridge-contracts-0.15.9.tgz#279f78ac434d5df69ccf2e51c01e57484789e91e" @@ -8053,7 +8071,7 @@ axios-extensions@3.1.7: tslib "^2.1.0" util "^0.12.3" -axios@0.21.1, axios@0.21.4, axios@0.26.1, axios@1.7.2, axios@^0.21.0, axios@^0.21.1, axios@^0.21.2, axios@^0.26.1, axios@^0.27.2, axios@^1.6.0, axios@^1.6.2, axios@^1.6.7, axios@^1.6.8: +axios@0.21.1, axios@0.21.4, axios@0.26.1, axios@1.7.2, axios@1.7.4, axios@^0.21.0, axios@^0.21.1, axios@^0.21.2, axios@^0.26.1, axios@^0.27.2, axios@^1.6.0, axios@^1.6.2, axios@^1.6.7, axios@^1.6.8: version "0.26.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== @@ -16830,7 +16848,7 @@ string-env-interpolation@^1.0.1: resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16848,6 +16866,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -16876,7 +16903,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -16897,6 +16924,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -18300,7 +18334,7 @@ wordwrapjs@^5.1.0: resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-5.1.0.tgz#4c4d20446dcc670b14fa115ef4f8fd9947af2b3a" integrity sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -18327,6 +18361,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 368135636a02a6c39887b38a55a7b824b810ede8 Mon Sep 17 00:00:00 2001 From: vuonghuuhung Date: Thu, 2 Jan 2025 14:05:32 +0700 Subject: [PATCH 2/2] remove unnecessary console log in NewTokenModal component --- src/pages/Pools/NewTokenModal/NewTokenModal.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/Pools/NewTokenModal/NewTokenModal.tsx b/src/pages/Pools/NewTokenModal/NewTokenModal.tsx index 6add7353d..6ce1797a8 100644 --- a/src/pages/Pools/NewTokenModal/NewTokenModal.tsx +++ b/src/pages/Pools/NewTokenModal/NewTokenModal.tsx @@ -86,8 +86,6 @@ const NewTokenModal: FC = ({ isOpen, close, open }) => { } ); - console.log((await wallet.getAccounts())[0]); - if (!oraiAddress) return displayToast(TToastType.TX_FAILED, { message: 'Wallet address does not exist!'