From 6022957f2ae4aa850f07cec862e6f8917a0f1363 Mon Sep 17 00:00:00 2001 From: nuanyang233 Date: Wed, 13 Apr 2022 17:41:39 +0800 Subject: [PATCH 1/3] feat: add transaction dialog in saving plugin --- .../Savings/SNSAdaptor/SavingsForm.tsx | 83 ++++++++++++++++--- .../plugins/Savings/protocols/AAVEProtocol.ts | 44 ++++++++-- .../plugins/Savings/protocols/LDOProtocol.ts | 45 ++++++++-- packages/mask/src/plugins/Savings/types.ts | 10 ++- 4 files changed, 154 insertions(+), 28 deletions(-) diff --git a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx index d700ba4748b7..124da94dcdc5 100644 --- a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx +++ b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx @@ -1,29 +1,31 @@ import BigNumber from 'bignumber.js' import { Typography } from '@mui/material' -import { useState, useMemo, useCallback } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useAsync, useAsyncFn } from 'react-use' import { unreachable } from '@dimensiondev/kit' import { isLessThan, rightShift } from '@masknet/web3-shared-base' import { + createContract, + createERC20Token, EthereumTokenType, - useFungibleTokenBalance, - useWeb3, - useAccount, - formatCurrency, formatBalance, + formatCurrency, + getAaveConstants, isSameAddress, + TransactionState, + TransactionStateType, + useAccount, + useFungibleTokenBalance, useTokenConstants, - createERC20Token, - getAaveConstants, + useWeb3, ZERO_ADDRESS, - createContract, } from '@masknet/web3-shared-evm' -import { TokenAmountPanel, FormattedCurrency, LoadingAnimation, TokenIcon } from '@masknet/shared' +import { FormattedCurrency, LoadingAnimation, TokenAmountPanel, TokenIcon } from '@masknet/shared' import { useRemoteControlledDialog } from '@masknet/shared-base-ui' import { useTokenPrice } from '../../Wallet/hooks/useTokenPrice' import { useI18N } from '../../../utils' import { useStyles } from './SavingsFormStyles' -import { TabType, ProtocolType, SavingsProtocol } from '../types' +import { ProtocolType, SavingsProtocol, TabType } from '../types' import { EthereumWalletConnectedBoundary } from '../../../web3/UI/EthereumWalletConnectedBoundary' import { EthereumChainBoundary } from '../../../web3/UI/EthereumChainBoundary' import { ActionButtonPromise } from '../../../extension/options-page/DashboardComponents/ActionButton' @@ -33,6 +35,10 @@ import { EthereumERC20TokenApprovedBoundary } from '../../../web3/UI/EthereumERC import type { AaveLendingPoolAddressProvider } from '@masknet/web3-contracts/types/AaveLendingPoolAddressProvider' import AaveLendingPoolAddressProviderABI from '@masknet/web3-contracts/abis/AaveLendingPoolAddressProvider.json' import type { AbiItem } from 'web3-utils' +import { WalletMessages } from '../../Wallet/messages' +import { isTwitter } from '../../../social-network-adaptor/twitter.com/base' +import { activatedSocialNetworkUI } from '../../../social-network' +import { isFacebook } from '../../../social-network-adaptor/facebook.com/base' export interface SavingsFormProps { chainId: number @@ -41,6 +47,11 @@ export interface SavingsFormProps { onClose?: () => void } +const ProtocolName = { + [ProtocolType.Lido]: 'lido', + [ProtocolType.AAVE]: 'aave', +} + export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProps) { const { t } = useI18N() const { classes } = useStyles() @@ -50,6 +61,9 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp const { NATIVE_TOKEN_ADDRESS } = useTokenConstants() const [inputAmount, setInputAmount] = useState('') const [estimatedGas, setEstimatedGas] = useState(new BigNumber('0')) + const [tradeState, setTradeState] = useState({ + type: TransactionStateType.UNKNOWN, + }) const { value: nativeTokenBalance } = useFungibleTokenBalance(EthereumTokenType.Native, '', chainId) @@ -151,10 +165,27 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp } }, [protocol.bareToken, inputAmount, chainId]) + const { setDialog: setTransactionDialog } = useRemoteControlledDialog( + WalletMessages.events.transactionDialogUpdated, + (ev) => { + if (ev.open) return + setTradeState({ + type: TransactionStateType.UNKNOWN, + }) + }, + ) + const [, executor] = useAsyncFn(async () => { switch (tab) { case TabType.Deposit: - if (!(await protocol.deposit(account, chainId, web3, tokenAmount))) { + setTradeState({ + type: TransactionStateType.WAIT_FOR_CONFIRMING, + }) + if ( + !(await protocol.deposit(account, chainId, web3, tokenAmount, (state) => { + setTradeState(state) + })) + ) { throw new Error('Failed to deposit token.') } else { await protocol.updateBalance(chainId, web3, account) @@ -179,6 +210,36 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp } }, [tab, protocol, account, chainId, web3, tokenAmount]) + useEffect(() => { + if (tradeState?.type === TransactionStateType.UNKNOWN) return + setTransactionDialog({ + open: true, + state: tradeState, + shareText: [ + `I just deposit ${inputAmount} ${protocol.bareToken.symbol} with ${ProtocolName[protocol.type]}. ${ + isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI) + ? `Follow @${ + isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account') + } (mask.io) to deposit.` + : '' + }`, + '#mask_io', + ].join('\n'), + }) + }, [tradeState]) + + console.log( + [ + `I just deposit ${inputAmount} ${protocol.bareToken.symbol} with ${ProtocolName[protocol.type]}. ${ + isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI) + ? `Follow @${ + isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account') + } (mask.io) to deposit it.` + : '' + }`, + '#mask_io', + ].join('\n'), + ) const needsSwap = protocol.type === ProtocolType.Lido && tab === TabType.Withdraw const buttonDom = useMemo(() => { diff --git a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts index 002047567ee4..0edbcb6131b7 100644 --- a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts +++ b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts @@ -4,9 +4,12 @@ import BigNumber from 'bignumber.js' import { pow10, ZERO } from '@masknet/web3-shared-base' import { ChainId, - getAaveConstants, createContract, FungibleTokenDetailed, + getAaveConstants, + TransactionEventType, + TransactionState, + TransactionStateType, ZERO_ADDRESS, } from '@masknet/web3-shared-evm' import type { AaveLendingPool } from '@masknet/web3-contracts/types/AaveLendingPool' @@ -179,19 +182,48 @@ export class AAVEProtocol implements SavingsProtocol { return contract?.methods.deposit(this.bareToken.address, new BigNumber(value).toFixed(), account, '0') } - public async deposit(account: string, chainId: ChainId, web3: Web3, value: BigNumber.Value) { + public async deposit( + account: string, + chainId: ChainId, + web3: Web3, + value: BigNumber.Value, + onChange: (state: TransactionState) => void, + ) { try { const gasEstimate = await this.depositEstimate(account, chainId, web3, value) const operation = await this.createDepositTokenOperation(account, chainId, web3, value) if (operation) { - await operation.send({ - from: account, - gas: gasEstimate.toNumber(), - }) + await operation + .send({ + from: account, + gas: gasEstimate.toNumber(), + }) + .on(TransactionEventType.ERROR, (error) => { + onChange({ + type: TransactionStateType.FAILED, + error: error, + }) + }) + .on(TransactionEventType.CONFIRMATION, (no, receipt) => { + onChange({ + type: TransactionStateType.CONFIRMED, + no, + receipt, + }) + }) + return true } + onChange({ + type: TransactionStateType.FAILED, + error: new Error("Can't create deposit operation"), + }) return false } catch (error) { + onChange({ + type: TransactionStateType.FAILED, + error: new Error('deposit failed'), + }) return false } } diff --git a/packages/mask/src/plugins/Savings/protocols/LDOProtocol.ts b/packages/mask/src/plugins/Savings/protocols/LDOProtocol.ts index 686eb9be3fc4..00f4079c4d32 100644 --- a/packages/mask/src/plugins/Savings/protocols/LDOProtocol.ts +++ b/packages/mask/src/plugins/Savings/protocols/LDOProtocol.ts @@ -3,15 +3,18 @@ import type { AbiItem } from 'web3-utils' import BigNumber from 'bignumber.js' import { ChainId, - getLidoConstants, createContract, FungibleTokenDetailed, + getLidoConstants, + TransactionEventType, + TransactionState, + TransactionStateType, ZERO_ADDRESS, } from '@masknet/web3-shared-evm' import { ZERO } from '@masknet/web3-shared-base' import type { Lido } from '@masknet/web3-contracts/types/Lido' import LidoABI from '@masknet/web3-contracts/abis/Lido.json' -import { SavingsProtocol, ProtocolType } from '../types' +import { ProtocolType, SavingsProtocol } from '../types' export class LidoProtocol implements SavingsProtocol { private _apr = '0.00' @@ -80,21 +83,45 @@ export class LidoProtocol implements SavingsProtocol { } } - public async deposit(account: string, chainId: ChainId, web3: Web3, value: BigNumber.Value) { + public async deposit( + account: string, + chainId: ChainId, + web3: Web3, + value: BigNumber.Value, + onChange: (state: TransactionState) => void, + ) { try { const contract = createContract( web3, getLidoConstants(chainId).LIDO_stETH_ADDRESS || ZERO_ADDRESS, LidoABI as AbiItem[], ) - await contract?.methods.submit(getLidoConstants(chainId).LIDO_REFERRAL_ADDRESS || ZERO_ADDRESS).send({ - from: account, - value: value.toString(), - gas: 300000, - }) - + await contract?.methods + .submit(getLidoConstants(chainId).LIDO_REFERRAL_ADDRESS || ZERO_ADDRESS) + .send({ + from: account, + value: value.toString(), + gas: 300000, + }) + .on(TransactionEventType.ERROR, (error) => { + onChange({ + type: TransactionStateType.FAILED, + error, + }) + }) + .on(TransactionEventType.CONFIRMATION, (no, receipt) => { + onChange({ + type: TransactionStateType.CONFIRMED, + no, + receipt, + }) + }) return true } catch (error) { + onChange({ + type: TransactionStateType.FAILED, + error: new Error('deposit failed'), + }) console.error('LDO `deposit()` Error', error) return false } diff --git a/packages/mask/src/plugins/Savings/types.ts b/packages/mask/src/plugins/Savings/types.ts index 5dec892e7314..6cad6d354e83 100644 --- a/packages/mask/src/plugins/Savings/types.ts +++ b/packages/mask/src/plugins/Savings/types.ts @@ -1,6 +1,6 @@ import type Web3 from 'web3' import type BigNumber from 'bignumber.js' -import type { ChainId, FungibleTokenDetailed } from '@masknet/web3-shared-evm' +import type { ChainId, FungibleTokenDetailed, TransactionState } from '@masknet/web3-shared-evm' export enum TabType { Deposit = 'deposit', @@ -36,7 +36,13 @@ export interface SavingsProtocol { updateBalance(chainId: ChainId, web3: Web3, account: string): Promise depositEstimate(account: string, chainId: ChainId, web3: Web3, value: BigNumber.Value): Promise - deposit(account: string, chainId: ChainId, web3: Web3, value: BigNumber.Value): Promise + deposit( + account: string, + chainId: ChainId, + web3: Web3, + value: BigNumber.Value, + onChange: (state: TransactionState) => void, + ): Promise withdrawEstimate(account: string, chainId: ChainId, web3: Web3, value: BigNumber.Value): Promise withdraw(account: string, chainId: ChainId, web3: Web3, value: BigNumber.Value): Promise } From 29b5ef3d8d525d131ab3c1e9c9c8072c6f22dcc7 Mon Sep 17 00:00:00 2001 From: nuanyang233 Date: Wed, 13 Apr 2022 20:53:26 +0800 Subject: [PATCH 2/3] fix: bugfix --- .../Savings/SNSAdaptor/SavingsForm.tsx | 14 +------- .../plugins/Savings/protocols/AAVEProtocol.ts | 36 ++++++++++--------- .../SearchableList/SearchableList.tsx | 2 +- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx index 124da94dcdc5..0f3ad66a7528 100644 --- a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx +++ b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx @@ -211,7 +211,7 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp }, [tab, protocol, account, chainId, web3, tokenAmount]) useEffect(() => { - if (tradeState?.type === TransactionStateType.UNKNOWN) return + if (tradeState.type === TransactionStateType.UNKNOWN) return setTransactionDialog({ open: true, state: tradeState, @@ -228,18 +228,6 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp }) }, [tradeState]) - console.log( - [ - `I just deposit ${inputAmount} ${protocol.bareToken.symbol} with ${ProtocolName[protocol.type]}. ${ - isTwitter(activatedSocialNetworkUI) || isFacebook(activatedSocialNetworkUI) - ? `Follow @${ - isTwitter(activatedSocialNetworkUI) ? t('twitter_account') : t('facebook_account') - } (mask.io) to deposit it.` - : '' - }`, - '#mask_io', - ].join('\n'), - ) const needsSwap = protocol.type === ProtocolType.Lido && tab === TabType.Withdraw const buttonDom = useMemo(() => { diff --git a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts index 0edbcb6131b7..3a40452c79ff 100644 --- a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts +++ b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts @@ -193,24 +193,28 @@ export class AAVEProtocol implements SavingsProtocol { const gasEstimate = await this.depositEstimate(account, chainId, web3, value) const operation = await this.createDepositTokenOperation(account, chainId, web3, value) if (operation) { - await operation - .send({ - from: account, - gas: gasEstimate.toNumber(), - }) - .on(TransactionEventType.ERROR, (error) => { - onChange({ - type: TransactionStateType.FAILED, - error: error, + await new Promise((resolve, reject) => { + operation + .send({ + from: account, + gas: gasEstimate.toNumber(), }) - }) - .on(TransactionEventType.CONFIRMATION, (no, receipt) => { - onChange({ - type: TransactionStateType.CONFIRMED, - no, - receipt, + .on(TransactionEventType.ERROR, (error) => { + onChange({ + type: TransactionStateType.FAILED, + error: error, + }) + reject(error) }) - }) + .on(TransactionEventType.CONFIRMATION, (no, receipt) => { + onChange({ + type: TransactionStateType.CONFIRMED, + no, + receipt, + }) + resolve(receipt.transactionHash) + }) + }) return true } diff --git a/packages/theme/src/Components/SearchableList/SearchableList.tsx b/packages/theme/src/Components/SearchableList/SearchableList.tsx index 0a55cb7ccae8..a1776821e765 100644 --- a/packages/theme/src/Components/SearchableList/SearchableList.tsx +++ b/packages/theme/src/Components/SearchableList/SearchableList.tsx @@ -72,7 +72,7 @@ export function SearchableList({ shouldSort: true, threshold: 0.45, minMatchCharLength: 1, - keys: searchKey ?? data.length > 0 ? Object.keys(data[0]) : [], + keys: searchKey ?? Object.keys(data.length > 0 ? data[0] : []), }), [data, searchKey], ) From dafee78cc8ea2d128701c0e59e780d0568bff5d8 Mon Sep 17 00:00:00 2001 From: nuanyang233 Date: Thu, 14 Apr 2022 14:03:24 +0800 Subject: [PATCH 3/3] fix: reset state when transaction has been confirmed --- .../Savings/SNSAdaptor/SavingsForm.tsx | 10 +++++- .../plugins/Savings/protocols/AAVEProtocol.ts | 36 +++++++++---------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx index 0f3ad66a7528..722340e13db4 100644 --- a/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx +++ b/packages/mask/src/plugins/Savings/SNSAdaptor/SavingsForm.tsx @@ -64,6 +64,7 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp const [tradeState, setTradeState] = useState({ type: TransactionStateType.UNKNOWN, }) + const [isOpen, setIsOpen] = useState(false) const { value: nativeTokenBalance } = useFungibleTokenBalance(EthereumTokenType.Native, '', chainId) @@ -183,7 +184,14 @@ export function SavingsForm({ chainId, protocol, tab, onClose }: SavingsFormProp }) if ( !(await protocol.deposit(account, chainId, web3, tokenAmount, (state) => { - setTradeState(state) + setTradeState((prev) => { + if ( + prev.type === TransactionStateType.UNKNOWN && + state.type === TransactionStateType.CONFIRMED + ) + return prev + return state + }) })) ) { throw new Error('Failed to deposit token.') diff --git a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts index 3a40452c79ff..0edbcb6131b7 100644 --- a/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts +++ b/packages/mask/src/plugins/Savings/protocols/AAVEProtocol.ts @@ -193,28 +193,24 @@ export class AAVEProtocol implements SavingsProtocol { const gasEstimate = await this.depositEstimate(account, chainId, web3, value) const operation = await this.createDepositTokenOperation(account, chainId, web3, value) if (operation) { - await new Promise((resolve, reject) => { - operation - .send({ - from: account, - gas: gasEstimate.toNumber(), + await operation + .send({ + from: account, + gas: gasEstimate.toNumber(), + }) + .on(TransactionEventType.ERROR, (error) => { + onChange({ + type: TransactionStateType.FAILED, + error: error, }) - .on(TransactionEventType.ERROR, (error) => { - onChange({ - type: TransactionStateType.FAILED, - error: error, - }) - reject(error) + }) + .on(TransactionEventType.CONFIRMATION, (no, receipt) => { + onChange({ + type: TransactionStateType.CONFIRMED, + no, + receipt, }) - .on(TransactionEventType.CONFIRMATION, (no, receipt) => { - onChange({ - type: TransactionStateType.CONFIRMED, - no, - receipt, - }) - resolve(receipt.transactionHash) - }) - }) + }) return true }