From 871dc6fe961bc5a3f1e2b1389368afda2d263ec7 Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Thu, 29 Feb 2024 16:07:53 +0700 Subject: [PATCH] Update get balance for account --- .../src/components/account/AccountList.tsx | 46 +++++--- .../src/components/layout/WalletHeader.tsx | 4 +- .../transaction/TransactionModal.tsx | 110 +++++++++++++----- packages/demo/src/pages/EvmWalletInfo.tsx | 11 +- packages/demo/src/pages/WalletInfo.tsx | 16 +-- packages/demo/src/pages/Welcome.tsx | 4 +- packages/demo/src/utils/api/evmApi.ts | 12 +- packages/demo/src/utils/api/substrateApi.ts | 13 ++- packages/demo/src/web3-onboard.ts | 2 +- 9 files changed, 148 insertions(+), 70 deletions(-) diff --git a/packages/demo/src/components/account/AccountList.tsx b/packages/demo/src/components/account/AccountList.tsx index ee2903e96..21ff38bc6 100644 --- a/packages/demo/src/components/account/AccountList.tsx +++ b/packages/demo/src/components/account/AccountList.tsx @@ -16,6 +16,8 @@ import { substrateApi } from "../../utils/api/substrateApi"; import {toShort} from "../../utils/style"; import TransactionModal from "../transaction/TransactionModal"; import styled from "styled-components"; +import {useNavigate} from "react-router-dom"; +import {TRANSACTION_MODAL} from "../../constants/modal"; @@ -32,11 +34,13 @@ type AccountMapType = { } - +const modalId = TRANSACTION_MODAL; function Component ({className, substrateProvider, evmProvider}: Props): React.ReactElement { const [{ wallet},] = useConnectWallet(); const renderEmpty = useCallback(() => , []); const [ accountsMap, setAccountMap ] = useState([]) + const navigate = useNavigate(); + const [ accountTransaction, setAccountTransaction ] = useState(); const [{ chains }] = useSetChain(); const [, customNotification, updateNotify,] = useNotifications(); const { activeModal } = useContext(ModalContext); @@ -79,15 +83,19 @@ function Component ({className, substrateProvider, evmProvider}: Props): React.R ); const onTransactionClicked = useCallback( - (address_: string) => { + (address: string) => { return async () => { - activeModal(`${address_}`); + const account = wallet?.accounts.find(({address: address_}) => address === address_); + setAccountTransaction(account) + account && activeModal(modalId); }; - }, [wallet]) + }, [activeModal, wallet]) + useEffect(() => { + const accountMap = wallet?.accounts.reduce((acc, account, index)=>{ acc.push({address: account.address, index, name: account.uns?.name || account.ens?.name || toShort(account.address)}) return acc @@ -128,7 +136,6 @@ function Component ({className, substrateProvider, evmProvider}: Props): React.R ) - const account = wallet?.accounts.find(({address: address_}) => address === address_) return( <> - {account && } ) - }, [wallet?.accounts, onSignClicked, onTransactionClicked, substrateProvider, evmProvider]) + }, [onSignClicked, onTransactionClicked]) return ( - + <> + { + accountsMap.length > 0 && + <> + + { + accountTransaction && + } + + + } + + ); } diff --git a/packages/demo/src/components/layout/WalletHeader.tsx b/packages/demo/src/components/layout/WalletHeader.tsx index ce0499e7f..c011c7889 100644 --- a/packages/demo/src/components/layout/WalletHeader.tsx +++ b/packages/demo/src/components/layout/WalletHeader.tsx @@ -51,7 +51,7 @@ function Component ({ visible, className }: Props): React.ReactElement { try { await set({ chainId: network.id, chainNamespace: network.namespace }) }catch (e) { - + console.log(e) } } } @@ -94,7 +94,7 @@ function Component ({ visible, className }: Props): React.ReactElement { modalId={modalId} onSelectItem={onSwitchNetwork} /> - + {wallet?.accounts && wallet.accounts.length > 0 && } : diff --git a/packages/demo/src/components/transaction/TransactionModal.tsx b/packages/demo/src/components/transaction/TransactionModal.tsx index 8a5dfad22..f8475baba 100644 --- a/packages/demo/src/components/transaction/TransactionModal.tsx +++ b/packages/demo/src/components/transaction/TransactionModal.tsx @@ -1,12 +1,12 @@ import { ThemeProps, TransferParams, FormCallbacks, Theme } from "../../types"; import {TRANSACTION_MODAL} from "../../constants/modal"; import { BaseModal} from "../modal"; -import { Button, Form, Icon, Input, ModalContext } from '@subwallet/react-ui'; +import { Button, Form, Icon, Input, ModalContext, Number, ActivityIndicator } from '@subwallet/react-ui'; import { useState, useCallback, useMemo, useEffect, useContext } from "react"; import { useConnectWallet, useNotifications, useSetChain } from "@subwallet_connect/react"; import { Rule } from '@subwallet/react-ui/es/form'; import { useWatchTransaction } from "../../hooks"; -import styled from 'styled-components'; +import styled, {useTheme} from 'styled-components'; import BigN from 'bignumber.js'; import { isAddress, isEthereumAddress } from '@polkadot/util-crypto'; import CN from "classnames"; @@ -14,9 +14,11 @@ import AccountBriefInfo from "../account/AccountBriefInfo"; import type { Account } from '@subwallet_connect/core/dist/types'; import { PaperPlaneTilt } from "@phosphor-icons/react"; import { NetworkInfo } from "../../utils/network"; -import {EIP1193Provider, SubstrateProvider} from "@subwallet_connect/common"; +import { EIP1193Provider, SubstrateProvider } from "@subwallet_connect/common"; import { substrateApi } from "../../utils/api/substrateApi"; import { evmApi } from "../../utils/api/evmApi"; +import BN from "bn.js"; +import {BN_ZERO} from "@polkadot/util"; export interface Props extends ThemeProps { senderAccount: Account; @@ -24,22 +26,54 @@ export interface Props extends ThemeProps { evmProvider ?: evmApi, }; - - +export interface TypeFreeBalance { + value: string, + symbol: string, + decimals: number +} +const DEFAULT_ADDRESS = '5GnUABVD7kt1wnmLiSeGcuSd5ESvmVnAjdMRrtvKxUGxuy6N' +const modalId = TRANSACTION_MODAL; function Component ({ className, senderAccount, evmProvider, substrateProvider }: Props) { const [{ wallet},] = useConnectWallet(); const [{ chains }] = useSetChain(); - const modalId = useMemo(() => `${senderAccount.address}`, [senderAccount.address, wallet]) const [loading, setLoading] = useState(false); const [, customNotification, updateNotify,] = useNotifications(); + const [ availableBalance, setAvailableBalance ] = useState() + const [ onReadyFreeBalance, setOnReadyFreeBalance ] = useState(false); const [ validatePass, setValidatePass ] = useState<{ to: boolean, amount: boolean}>({ to: false, amount: false }) - const [ defaultData, persistData ] = useState({ + const defaultData = useMemo((): TransferParams => { + return ({ from: senderAccount.address, to: '', value: '' - } - ); - const { inactiveModal } = useContext(ModalContext); + }) + }, [senderAccount]) + const { token } = useTheme() as Theme; + const { inactiveModal, checkActive } = useContext(ModalContext); + + const getMaxTransfer = useCallback(async () => { + if(!wallet) return ; + setOnReadyFreeBalance(false); + substrateProvider && await substrateProvider.isReady(); + + const { namespace: namespace_, id: chainId } = wallet.chains[0] + const chainInfo = chains.find(({id, namespace}) => id === chainId && namespace === namespace_); + const maxTransfer = (await (substrateProvider || evmProvider)?.getMaxTransfer( '0', senderAccount.address, DEFAULT_ADDRESS)) || '0'; + const maxTransferBN = new BN(maxTransfer); + setAvailableBalance( +{ + symbol: chainInfo?.token || ( wallet.type === 'evm' ? 'ETH' : 'DOT') , + value: maxTransferBN.gt(BN_ZERO) ? maxTransfer : '0', + decimals: chainInfo?.decimal || ( wallet.type === 'evm' ? 18 : 10) + } + ) + setOnReadyFreeBalance(true); + + }, [ wallet?.chains[0], substrateProvider, evmProvider, senderAccount.address]) + + useEffect(() => { + checkActive(modalId) && getMaxTransfer().then(r => {}); + }, [checkActive(modalId), wallet?.chains[0].id, senderAccount.address]); @@ -50,13 +84,6 @@ function Component ({ className, senderAccount, evmProvider, substrateProvider } const transferAmount = useWatchTransaction('value', form, defaultData); const to = useWatchTransaction('to', form, defaultData); - useEffect(() => { - persistData({ - from: senderAccount.address, - to: '', - value: '' - }) - }, [senderAccount]); const validateRecipientAddress = useCallback(async (rule: Rule, _recipientAddress: string): Promise => { @@ -141,8 +168,14 @@ function Component ({ className, senderAccount, evmProvider, substrateProvider } const onCloseModal = useCallback(() => { - inactiveModal(modalId) - }, [modalId, inactiveModal]) + setAvailableBalance(undefined); + inactiveModal(modalId); + form.resetFields(['to', 'value']); + }, [ inactiveModal, form]) + + const onValueChange = useCallback(() => { + + }, []) @@ -164,17 +197,6 @@ function Component ({ className, senderAccount, evmProvider, substrateProvider } if(wallet?.type === "evm"){ blockHash = await evmProvider?.sendTransaction(senderAccount.address, to, amount ) || '' }else{ - const ws = NetworkInfo[chainInfo.label as string].wsProvider; - if(! ws) { - const {} = customNotification({ - type: 'error', - message: - 'This network is not provide api', - autoDismiss: 2000 - }) - - return ; - } const getSigner = async ()=>{ const provider = wallet.provider as SubstrateProvider; @@ -245,6 +267,7 @@ function Component ({ className, senderAccount, evmProvider, substrateProvider } id={modalId} title={'Transaction'} closable={true} + maskClosable={false} onCancel={onCloseModal} className={CN(className, 'transaction-modal')} fullSizeOnMobile={true} @@ -253,6 +276,7 @@ function Component ({ className, senderAccount, evmProvider, substrateProvider }
@@ -307,6 +331,23 @@ function Component ({ className, senderAccount, evmProvider, substrateProvider } suffix={suffixAmountInput} /> + +
+ Sender available balance: + { + availableBalance && onReadyFreeBalance? + : + + } +
@@ -397,6 +438,15 @@ const TransactionModal = styled(Component)(({ theme: {token} }) => { '.__amount-transfer-input': { '-moz-appearance': 'textfield' + }, + '.__balance-transferable-item': { + display: 'flex', + flexWrap: 'wrap', + color: token.colorTextTertiary, + + '.__label-balance-transferable': { + marginRight: 3 + }, } }); diff --git a/packages/demo/src/pages/EvmWalletInfo.tsx b/packages/demo/src/pages/EvmWalletInfo.tsx index 3efc0a72e..2867ad930 100644 --- a/packages/demo/src/pages/EvmWalletInfo.tsx +++ b/packages/demo/src/pages/EvmWalletInfo.tsx @@ -23,10 +23,11 @@ interface Props extends ThemeProps{}; function Component ({className}: Props): React.ReactElement { - const [{ wallet, connecting},connect,, updateBalance] = useConnectWallet(); + const [{ wallet},,disconnect] = useConnectWallet(); const [{ chains}, setChain] = useSetChain(); const navigate = useNavigate(); const [ evmProvider, setEvmProvider ] = useState(); + const [ listendAccountChanged, setListendAccounChanged ] = useState(false); const customNotification = useNotifications()[1]; const { isWebUI } = useContext(ScreenContext); @@ -36,11 +37,11 @@ function Component ({className}: Props): React.ReactElement { if(!wallet) return; setEvmProvider(new evmApi(wallet.provider as EIP1193Provider)); wallet.provider.on('accountsChanged', (accounts) => { - if(accounts.length === 0){ - navigate("/welcome"); + if(!accounts || accounts.length === 0 ){ + disconnect({ label: wallet.label, type: wallet.type }) } }) - }, [wallet]); + }, [wallet, navigate]); const requestPermission = useCallback(async ()=> { @@ -79,7 +80,7 @@ function Component ({className}: Props): React.ReactElement {
Account List
- + {wallet?.accounts && wallet.accounts.length > 0 && }
Permission
diff --git a/packages/demo/src/pages/WalletInfo.tsx b/packages/demo/src/pages/WalletInfo.tsx index 6a4965282..c2e13ac92 100644 --- a/packages/demo/src/pages/WalletInfo.tsx +++ b/packages/demo/src/pages/WalletInfo.tsx @@ -20,7 +20,7 @@ interface Props extends ThemeProps {}; function Component ({className}: Props): React.ReactElement { const navigate = useNavigate(); - const [ { wallet}] = useConnectWallet(); + const [ { wallet},, disconnect] = useConnectWallet(); const [ substrateProvider, setSubstrateProvider ] = useState(); const [{ chains }] = useSetChain(); const { isWebUI } = useContext(ScreenContext); @@ -37,12 +37,12 @@ function Component ({className}: Props): React.ReactElement { } } - wallet.provider.on('accountsChanged', (accounts) => { - if(accounts.length === 0){ - navigate("/welcome"); - } - }) - }, [wallet]); + wallet.provider.on('accountsChanged', (accounts) => { + if(!accounts || accounts.length === 0 ){ + disconnect({ label: wallet.label, type: wallet.type }) + } + }) + }, [wallet, navigate]); return (
Account List
- + {wallet?.accounts && wallet.accounts.length > 0 && }
{!! wallet?.metadata && diff --git a/packages/demo/src/pages/Welcome.tsx b/packages/demo/src/pages/Welcome.tsx index c397ddccd..4cb03b641 100644 --- a/packages/demo/src/pages/Welcome.tsx +++ b/packages/demo/src/pages/Welcome.tsx @@ -24,9 +24,9 @@ function Component ({ className }: Props): React.ReactElement { const [{ wallet }, connect] = useConnectWallet() const { token } = useTheme() as Theme; useEffect(() => { - if (wallet?.type === 'substrate') { + if (wallet?.type === 'substrate' && wallet.accounts.length > 0) { navigate('/wallet-info'); - } else if(wallet?.type === 'evm' ){ + } else if(wallet?.type === 'evm' && wallet.accounts.length > 0 ){ navigate('/evm-wallet-info'); } }, [wallet]); diff --git a/packages/demo/src/utils/api/evmApi.ts b/packages/demo/src/utils/api/evmApi.ts index d0ae1d099..6d19ce894 100644 --- a/packages/demo/src/utils/api/evmApi.ts +++ b/packages/demo/src/utils/api/evmApi.ts @@ -13,6 +13,12 @@ export class evmApi { this.provider = new ethers.providers.Web3Provider(provider, 'any') } + public async getMaxTransfer (amount: string, senderAddress: string, recipientAddress: string) { + if(!this.provider) return '0'; + + return (await this.provider.getBalance(senderAddress)).toString(); + } + public async isAvailableAmount ( amount: string, senderAddress: string, recipientAddress: string ) { if(!this.provider) return false; @@ -21,14 +27,14 @@ export class evmApi { value: amount } - const [ gas, price, balance ] = await Promise.all([ + const [ gas, price ] = await Promise.all([ this.provider.getGasPrice().then(res => new BigNumber(res.toString())), this.provider.estimateGas(txDetails).then(res => new BigNumber(res.toString())), - this.provider.getBalance(senderAddress) ]) const transactionCost = gas.times(price).plus(amount); + const balance = new BigNumber(await this.getMaxTransfer(amount, senderAddress, recipientAddress)); - return new BigNumber(balance.toString()).gt(transactionCost); + return balance.gt(transactionCost) && balance.gt(new BigNumber(amount)); } public async sendTransaction (senderAddress: string, recipientAddress: string, amount: string ) { if(! this.provider) return; diff --git a/packages/demo/src/utils/api/substrateApi.ts b/packages/demo/src/utils/api/substrateApi.ts index b478b7c3b..05d234d05 100644 --- a/packages/demo/src/utils/api/substrateApi.ts +++ b/packages/demo/src/utils/api/substrateApi.ts @@ -22,8 +22,8 @@ export class substrateApi { return this.api?.isReady } - public async isAvailableAmount ( amount: string, senderAddress: string, recipientAddress: string ) { - if(!this.api || !this.api.isReady ) return false; + public async getMaxTransfer (amount: string, senderAddress: string, recipientAddress: string) { + if(!this.api || !this.api.isReady ) return '0'; const transferExtrinsic = this.api.tx.balances.transferKeepAlive(recipientAddress, amount) const [ { partialFee }, balances ] = await Promise.all([ @@ -33,9 +33,14 @@ export class substrateApi { const adjFee = partialFee.muln(110).div(BN_HUNDRED); const maxTransfer = balances.availableBalance.sub(adjFee); - console.log(maxTransfer.toString()); - return !!(maxTransfer.gt(new BN(this.api?.consts.balances.existentialDeposit as any))) + return maxTransfer.toString(); + } + + public async isAvailableAmount ( amount: string, senderAddress: string, recipientAddress: string ) { + + const maxTransfer = new BN( await this.getMaxTransfer(amount, senderAddress, recipientAddress)); + return !!(maxTransfer.gt(new BN(this.api?.consts.balances.existentialDeposit as any)) && maxTransfer.gt(new BN(amount))) } diff --git a/packages/demo/src/web3-onboard.ts b/packages/demo/src/web3-onboard.ts index e2fba3321..c07cebe64 100644 --- a/packages/demo/src/web3-onboard.ts +++ b/packages/demo/src/web3-onboard.ts @@ -108,7 +108,7 @@ export default init({ // used for display, eg Ethereum Mainnet label: 'Ethereum Mainnet', // used for network requests - rpcUrl: `https://mainnet.infura.io/v3/${INFURA_KEY}`, + rpcUrl: `https://ethereum.publicnode.com`, decimal: 18 }, {