From d82acb547ba207eee8d4d7ae3c31cd14deb93d2d Mon Sep 17 00:00:00 2001 From: guanbinrui Date: Sun, 12 Jun 2022 21:48:14 +0800 Subject: [PATCH 1/6] chore: impl tx watcher --- .../WalletStatusBox/TransactionList.tsx | 4 +- .../Gitcoin/SNSAdaptor/DonateDialog.tsx | 2 +- .../src/web3-state/TransactionWatcher.ts | 171 ++++++++++++------ .../src/web3/useClearTransactionsCallback.ts | 13 +- .../src/web3/useRemoveTransaction.ts | 3 +- .../state/Connection/providers/MaskWallet.ts | 6 +- .../EVM/src/state/TransactionWatcher.ts | 23 ++- .../checkers/AccountChecker.ts | 5 + .../checkers/ReceiptChecker.ts | 12 +- packages/plugins/EVM/src/state/index.ts | 4 +- .../shared-base/src/utils/subscription.ts | 31 +++- .../web3-providers/src/MagicEden/index.ts | 4 +- packages/web3-providers/src/opensea/index.ts | 4 +- packages/web3-providers/src/rarible/index.ts | 4 +- packages/web3-shared/base/src/specs/index.ts | 2 +- 15 files changed, 206 insertions(+), 82 deletions(-) diff --git a/packages/mask/src/components/shared/WalletStatusBox/TransactionList.tsx b/packages/mask/src/components/shared/WalletStatusBox/TransactionList.tsx index 8e21593ef2bb..9e030ec6a7bb 100644 --- a/packages/mask/src/components/shared/WalletStatusBox/TransactionList.tsx +++ b/packages/mask/src/components/shared/WalletStatusBox/TransactionList.tsx @@ -109,12 +109,12 @@ const Transaction: FC = ({ chainId, transaction: tx, onClear = const [txStatus, setTxStatus] = useState(tx.status) useEffect(() => { - const removeListener = TransactionWatcher?.emitter.on('progress', (id, status, transaction) => { + const off = TransactionWatcher?.emitter.on('progress', (id, status, transaction) => { setTxStatus(status) }) return () => { - removeListener?.() + off?.() } }, [tx.id, TransactionWatcher]) diff --git a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx index d8999c95dad8..4784898a1bae 100644 --- a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx +++ b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx @@ -75,7 +75,7 @@ export function DonateDialog(props: DonateDialogProps) { nativeTokenDetailed.value, ) - const tokenBalance = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM) + const tokenBalance = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM, nativeTokenDetailed.value?.address) // #region select token dialog const pickToken = usePickToken() diff --git a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts index a122592e0f0b..5b55f72e7d44 100644 --- a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts +++ b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts @@ -1,4 +1,7 @@ +import { omit } from 'lodash-unified' +import type { Subscription } from 'use-subscription' import { Emitter } from '@servie/events' +import { getSubscriptionCurrentValue, StorageItem } from '@masknet/shared-base' import { TransactionChecker, TransactionStatusType, @@ -7,7 +10,7 @@ import { } from '@masknet/web3-shared-base' import type { Plugin } from '../types' -interface StorageItem { +export interface TransactionWatcherItem { at: number id: string chainId: ChainId @@ -15,77 +18,100 @@ interface StorageItem { transaction: Transaction } -class Storage { +export type TransactionWatcher = Record< + // @ts-ignore + ChainId, + Record< + // transaction id + string, + TransactionWatcherItem + > +> + +class Watcher { + static LATEST_TRANSACTION_SIZE = 5 static MAX_ITEM_SIZE = 40 - private map = new Map>>() + private timer: NodeJS.Timeout | null = null + + constructor( + protected storage: StorageItem>, + protected checkers: Array>, + protected options: { + delay: number + onNotify: (id: string, status: TransactionStatusType, transaction: Transaction) => void + }, + ) {} private getStorage(chainId: ChainId) { - if (!this.map.has(chainId)) this.map.set(chainId, new Map()) - return this.map.get(chainId)! + return this.storage.value[chainId] } - public hasItem(chainId: ChainId, id: string) { - return this.getStorage(chainId).has(id) + private setStorage(chainId: ChainId, id: string, item: TransactionWatcherItem) { + this.storage.setValue({ + ...this.storage.value, + // @ts-ignore + [chainId]: { + ...this.storage.value[chainId], + [item.id]: item, + }, + }) } - public getItem(chainId: ChainId, id: string) { - return this.getStorage(chainId).get(id) + private deleteStorage(chainId: ChainId, id: string) { + this.storage.setValue({ + ...this.storage.value, + // @ts-ignore + [chainId]: omit(this.storage.value[chainId], [id]), + }) } - public setItem(chainId: ChainId, id: string, transaction: StorageItem) { - this.getStorage(chainId).set(id, transaction) + private setTransaction(chainId: ChainId, id: string, transaction: TransactionWatcherItem) { + this.setStorage(chainId, id, transaction) } - public removeItem(chainId: ChainId, id: string) { - this.getStorage(chainId).delete(id) + private removeTransaction(chainId: ChainId, id: string) { + this.deleteStorage(chainId, id) } - public getItems(chainId: ChainId) { - const map = this.getStorage(chainId) - return map ? [...map.entries()].sort(([, a], [, z]) => z.at - a.at) : [] + private getAllTransactions(chainId: ChainId) { + const storage = this.getStorage(chainId) + return storage ? [...Object.entries(storage)].sort(([, a], [, z]) => z.at - a.at) : [] } - public getWatched(chainId: ChainId) { - return this.getItems(chainId).slice(0, Storage.MAX_ITEM_SIZE) + private getWatched(chainId: ChainId) { + return this.getAllTransactions(chainId).slice(0, Watcher.MAX_ITEM_SIZE) } - public getUnwatched(chainId: ChainId) { - return this.getItems(chainId).slice(Storage.MAX_ITEM_SIZE) + private getUnwatched(chainId: ChainId) { + return this.getAllTransactions(chainId).slice(Watcher.MAX_ITEM_SIZE) } -} - -class Watcher { - static LATEST_TRANSACTION_SIZE = 5 - - private timer: NodeJS.Timeout | null = null - private storage = new Storage() - - constructor( - protected checkers: Array>, - protected options: { - delay: number - onNotify: (id: string, status: TransactionStatusType, transaction: Transaction) => void - }, - ) {} private async check(chainId: ChainId) { // stop any pending task this.stopCheck() // unwatch legacy transactions - this.storage.getUnwatched(chainId).forEach(([id]) => this.unwatchTransaction(chainId, id)) + this.getUnwatched(chainId).forEach(([id]) => this.unwatchTransaction(chainId, id)) // check if all transactions were sealed - const watchedTransactions = this.storage - .getWatched(chainId) - .filter(([, x]) => x.status !== TransactionStatusType.NOT_DEPEND) + const watchedTransactions = this.getWatched(chainId).filter( + ([, x]) => x.status === TransactionStatusType.NOT_DEPEND, + ) if (!watchedTransactions.length) return for (const [id, { transaction }] of watchedTransactions) { for (const checker of this.checkers) { - const status = await checker.checkStatus(chainId, id) - if (status !== TransactionStatusType.NOT_DEPEND) this.options.onNotify(id, status, transaction) + try { + const status = await checker.checkStatus(chainId, id) + if (status !== TransactionStatusType.NOT_DEPEND) { + this.removeTransaction(chainId, id) + this.options.onNotify(id, status, transaction) + } + } catch (error) { + console.log('DEBUG: check error') + console.log(error) + } } } @@ -93,57 +119,87 @@ class Watcher { this.startCheck(chainId) } - private startCheck(chainId: ChainId) { + public startCheck(chainId: ChainId) { this.stopCheck() if (this.timer === null) { this.timer = setTimeout(this.check.bind(this, chainId), this.options.delay) } } - private stopCheck() { + public stopCheck() { if (this.timer !== null) clearTimeout(this.timer) this.timer = null } public watchTransaction(chainId: ChainId, id: string, transaction: Transaction) { - if (!this.storage.hasItem(chainId, id)) { - this.storage.setItem(chainId, id, { - at: Date.now(), - id, - chainId, - status: TransactionStatusType.NOT_DEPEND, - transaction, - }) - } + this.setTransaction(chainId, id, { + at: Date.now(), + id, + chainId, + status: TransactionStatusType.NOT_DEPEND, + transaction, + }) this.startCheck(chainId) } public unwatchTransaction(chainId: ChainId, id: string) { - this.storage.removeItem(chainId, id) + this.removeTransaction(chainId, id) } } export class TransactionWatcherState implements Web3TransactionWatcherState { + protected storage: StorageItem> = null! private watchers: Map> = new Map() emitter: Emitter> = new Emitter() constructor( protected context: Plugin.Shared.SharedContext, + protected chainIds: ChainId[], protected checkers: Array>, + protected subscriptions: { + chainId?: Subscription + }, protected options: { /** Default block delay in seconds */ defaultBlockDelay: number }, - ) {} + ) { + const defaultValue = Object.fromEntries(chainIds.map((x) => [x, {}])) as TransactionWatcher< + ChainId, + Transaction + > + const { storage } = this.context.createKVStorage('memory', {}).createSubScope('TransactionWatcher', { + value: defaultValue, + }) + + this.storage = storage.value + + if (this.subscriptions.chainId) { + const resume = () => { + const chainId = this.subscriptions.chainId?.getCurrentValue() + if (chainId) this.resumeWatcher(chainId) + } + + // resume watcher if chain id changed + this.subscriptions.chainId.subscribe(() => resume()) + + // storage has set up, resume watcher + getSubscriptionCurrentValue(() => { + return this.subscriptions.chainId + }).then(() => { + resume() + }) + } + } private getWatcher(chainId: ChainId) { if (!this.watchers.has(chainId)) this.watchers.set( chainId, - new Watcher(this.checkers, { + new Watcher(this.storage, this.checkers, { delay: this.options.defaultBlockDelay * 1000, onNotify: this.notifyTransaction.bind(this), }), @@ -151,6 +207,11 @@ export class TransactionWatcherState return this.watchers.get(chainId)! } + private resumeWatcher(chainId: ChainId) { + const watcher = this.getWatcher(chainId) + watcher.startCheck(chainId) + } + watchTransaction(chainId: ChainId, id: string, transaction: Transaction) { this.getWatcher(chainId).watchTransaction(chainId, id, transaction) this.emitter.emit('progress', id, TransactionStatusType.NOT_DEPEND, transaction) diff --git a/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts b/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts index e02220dbfaad..b8b5d1c26c9e 100644 --- a/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts +++ b/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts @@ -3,14 +3,25 @@ import type { NetworkPluginID } from '@masknet/web3-shared-base' import { useAccount } from './useAccount' import { useChainId } from './useChainId' import { useWeb3State } from './useWeb3State' +import { getSubscriptionCurrentValue } from '@masknet/shared-base' export function useClearTransactionsCallback(pluginID?: T) { const account = useAccount(pluginID) const chainId = useChainId(pluginID) - const { Transaction } = useWeb3State(pluginID) + const { Transaction, TransactionWatcher } = useWeb3State(pluginID) return useCallback(async () => { if (!account) return + + try { + const transactions = await getSubscriptionCurrentValue(() => Transaction?.transactions) + transactions + ?.flatMap((x) => Object.keys(x.candidates)) + .forEach((x) => TransactionWatcher?.unwatchTransaction(chainId, x)) + } catch { + console.warn('Failed to unwatch transaction.') + } + return Transaction?.clearTransactions?.(chainId, account) }, [chainId, account, Transaction]) } diff --git a/packages/plugin-infra/src/web3/useRemoveTransaction.ts b/packages/plugin-infra/src/web3/useRemoveTransaction.ts index f6b1de675720..4d19852cbcb7 100644 --- a/packages/plugin-infra/src/web3/useRemoveTransaction.ts +++ b/packages/plugin-infra/src/web3/useRemoveTransaction.ts @@ -7,11 +7,12 @@ import { useWeb3State } from './useWeb3State' export function useRemoveTransactionCallback(pluginID?: T) { const account = useAccount(pluginID) const chainId = useChainId(pluginID) - const { Transaction } = useWeb3State(pluginID) + const { Transaction, TransactionWatcher } = useWeb3State(pluginID) return useCallback( async (id: string) => { if (!account) return + TransactionWatcher?.unwatchTransaction(chainId, id) return Transaction?.removeTransaction?.(chainId, account, id) }, [chainId, account, Transaction], diff --git a/packages/plugins/EVM/src/state/Connection/providers/MaskWallet.ts b/packages/plugins/EVM/src/state/Connection/providers/MaskWallet.ts index b8ef87153bd1..f94dd62015e4 100644 --- a/packages/plugins/EVM/src/state/Connection/providers/MaskWallet.ts +++ b/packages/plugins/EVM/src/state/Connection/providers/MaskWallet.ts @@ -9,8 +9,6 @@ import { first } from 'lodash-unified' import type { ProviderOptions } from '@masknet/web3-shared-base' export class MaskWalletProvider extends BaseProvider implements EVM_Provider { - private id = 0 - constructor() { super() Web3StateSettings.readyPromise.then(this.addSharedContextListeners.bind(this)) @@ -55,10 +53,8 @@ export class MaskWalletProvider extends BaseProvider implements EVM_Provider { requestArguments: RequestArguments, options?: ProviderOptions, ): Promise { - this.id += 1 - const response = await SharedContextSettings.value.send( - createPayload(this.id, requestArguments.method, requestArguments.params), + createPayload(0, requestArguments.method, requestArguments.params), { chainId: SharedContextSettings.value.chainId.getCurrentValue(), popupsWindow: getSiteType() === ExtensionSite.Dashboard || isEnhanceableSiteType(), diff --git a/packages/plugins/EVM/src/state/TransactionWatcher.ts b/packages/plugins/EVM/src/state/TransactionWatcher.ts index a7bcb4d47e6d..39f607ff5569 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher.ts @@ -1,12 +1,25 @@ +import { getEnumAsArray } from '@dimensiondev/kit' import type { Plugin } from '@masknet/plugin-infra' import { TransactionWatcherState } from '@masknet/plugin-infra/web3' -import type { ChainId, Transaction } from '@masknet/web3-shared-evm' +import { ChainId, Transaction } from '@masknet/web3-shared-evm' +import type { Subscription } from 'use-subscription' import { TransactionCheckers } from './TransactionWatcher/checker' export class TransactionWatcher extends TransactionWatcherState { - constructor(context: Plugin.Shared.SharedContext) { - super(context, TransactionCheckers, { - defaultBlockDelay: 15, - }) + constructor( + context: Plugin.Shared.SharedContext, + subscriptions: { + chainId?: Subscription + }, + ) { + super( + context, + getEnumAsArray(ChainId).map((x) => x.value), + TransactionCheckers, + subscriptions, + { + defaultBlockDelay: 15, + }, + ) } } diff --git a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts index bda76e0751cb..f3a4532d2f57 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts @@ -3,6 +3,11 @@ import type { ChainId } from '@masknet/web3-shared-evm' export class AccountChecker implements TransactionChecker { checkStatus(chainId: ChainId, id: string): Promise { + console.log('DEBUG: account checker') + console.log({ + chainId, + id, + }) throw new Error('Method not implemented.') } } diff --git a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts index 8d13da095e47..c34677653a24 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts @@ -1,8 +1,14 @@ -import type { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' +import { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' import type { ChainId } from '@masknet/web3-shared-evm' +import { Web3StateSettings } from '../../../settings' +import { getReceiptStatus } from '../../Connection/utils' export class ReceiptChecker implements TransactionChecker { - checkStatus(chainId: ChainId, id: string): Promise { - throw new Error('Method not implemented.') + async checkStatus(chainId: ChainId, id: string): Promise { + const connection = await Web3StateSettings.value.Connection?.getConnection?.({ + chainId, + }) + const receipt = await connection?.getTransactionReceipt(id) + return getReceiptStatus(receipt ?? null) ?? TransactionStatusType.NOT_DEPEND } } diff --git a/packages/plugins/EVM/src/state/index.ts b/packages/plugins/EVM/src/state/index.ts index 606c9c0ffd55..d0b158b09be0 100644 --- a/packages/plugins/EVM/src/state/index.ts +++ b/packages/plugins/EVM/src/state/index.ts @@ -49,7 +49,9 @@ export function createWeb3State(context: Plugin.Shared.SharedContext): EVM_Web3S account: Provider_.account, }), TransactionFormatter: new TransactionFormatter(context), - TransactionWatcher: new TransactionWatcher(context), + TransactionWatcher: new TransactionWatcher(context, { + chainId: Provider_.chainId, + }), Connection: new Connection(context, { chainId: Provider_.chainId, account: Provider_.account, diff --git a/packages/shared-base/src/utils/subscription.ts b/packages/shared-base/src/utils/subscription.ts index b393ab942212..428e83836470 100644 --- a/packages/shared-base/src/utils/subscription.ts +++ b/packages/shared-base/src/utils/subscription.ts @@ -1,8 +1,37 @@ -import { noop } from 'lodash-unified' +import { noop, reject } from 'lodash-unified' import type { ValueRef } from '@dimensiondev/holoflows-kit' import type { Subscription } from 'use-subscription' import { None, Option, Some } from 'ts-results' +export async function getSubscriptionCurrentValue( + getSubscription: () => Subscription | undefined, +): Promise { + const getValue = () => { + return getSubscription()?.getCurrentValue() + } + + const createReader = async () => { + try { + return getValue() + } catch (error: unknown) { + if (!(error instanceof Promise)) return + await error + return getValue() + } + } + + const createReaders = Array.from<() => Promise>({ length: 3 }).fill(() => createReader()) + + for (const createReader of createReaders) { + try { + return await createReader() + } catch { + continue + } + } + return +} + export function createConstantSubscription(value: T): Subscription { return { getCurrentValue: () => value, diff --git a/packages/web3-providers/src/MagicEden/index.ts b/packages/web3-providers/src/MagicEden/index.ts index 43109fee3790..f2bd5712c568 100644 --- a/packages/web3-providers/src/MagicEden/index.ts +++ b/packages/web3-providers/src/MagicEden/index.ts @@ -190,7 +190,7 @@ export class MagicEdenAPI implements NonFungibleTokenAPI.Provider ({ id: x.bid, chainId: ChainId.Mainnet, - asset_permalink: link, + assetPermalink: link, hash: x.bid, quantity: '1', createdAt: x.timestamp * 1000, @@ -263,7 +263,7 @@ export class MagicEdenAPI implements NonFungibleTokenAPI.Provider ({ id: x.order_hash, chainId, - asset_permalink: asset.opensea_link, + assetPermalink: asset.opensea_link, hash: x.order_hash, quantity: x.quantity, createdAt: x.created_time ? getUnixTime(new Date(x.created_time)) : undefined, @@ -243,7 +243,7 @@ function createAssetOrder(chainId: ChainId, order: OpenSeaAssetOrder): NonFungib return { id: order.order_hash, chainId, - asset_permalink: order.asset.opensea_link, + assetPermalink: order.asset.opensea_link, hash: order.order_hash, quantity: order.quantity, side: order.side, diff --git a/packages/web3-providers/src/rarible/index.ts b/packages/web3-providers/src/rarible/index.ts index 65adb44c9356..332db2d7a145 100644 --- a/packages/web3-providers/src/rarible/index.ts +++ b/packages/web3-providers/src/rarible/index.ts @@ -211,7 +211,7 @@ export class RaribleAPI implements NonFungibleTokenAPI.Provider { /** chain Id */ chainId: ChainId /** permalink of asset */ - asset_permalink: string + assetPermalink: string /** token amount */ quantity: string /** transaction hash */ From 3bf709870232e28fd670789de5c2420d4a56d3b2 Mon Sep 17 00:00:00 2001 From: guanbinrui Date: Mon, 13 Jun 2022 02:03:45 +0800 Subject: [PATCH 2/6] chore: receipt checker --- .../src/web3-state/TransactionWatcher.ts | 6 ++++- .../EVM/src/state/TransactionWatcher.ts | 1 + .../checkers/AccountChecker.ts | 24 ++++++++++++------- .../checkers/ReceiptChecker.ts | 2 +- .../shared-base/src/utils/subscription.ts | 4 ++-- packages/web3-shared/base/src/specs/index.ts | 2 +- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts index 5b55f72e7d44..c6b5ed80e8e6 100644 --- a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts +++ b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts @@ -39,6 +39,7 @@ class Watcher { protected checkers: Array>, protected options: { delay: number + getCreatorAddress: (transaction: Transaction) => string onNotify: (id: string, status: TransactionStatusType, transaction: Transaction) => void }, ) {} @@ -103,7 +104,7 @@ class Watcher { for (const [id, { transaction }] of watchedTransactions) { for (const checker of this.checkers) { try { - const status = await checker.checkStatus(chainId, id) + const status = await checker.checkStatus(id, chainId, this.options.getCreatorAddress(transaction)) if (status !== TransactionStatusType.NOT_DEPEND) { this.removeTransaction(chainId, id) this.options.onNotify(id, status, transaction) @@ -165,6 +166,8 @@ export class TransactionWatcherState protected options: { /** Default block delay in seconds */ defaultBlockDelay: number + /** Get the author address */ + getCreatorAddress: (transaction: Transaction) => string }, ) { const defaultValue = Object.fromEntries(chainIds.map((x) => [x, {}])) as TransactionWatcher< @@ -202,6 +205,7 @@ export class TransactionWatcherState new Watcher(this.storage, this.checkers, { delay: this.options.defaultBlockDelay * 1000, onNotify: this.notifyTransaction.bind(this), + getCreatorAddress: this.options.getCreatorAddress, }), ) return this.watchers.get(chainId)! diff --git a/packages/plugins/EVM/src/state/TransactionWatcher.ts b/packages/plugins/EVM/src/state/TransactionWatcher.ts index 39f607ff5569..991f6e99f6ce 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher.ts @@ -19,6 +19,7 @@ export class TransactionWatcher extends TransactionWatcherState (tx.from as string | undefined) ?? '', }, ) } diff --git a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts index f3a4532d2f57..64c7556fb47d 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts @@ -1,13 +1,21 @@ -import type { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' -import type { ChainId } from '@masknet/web3-shared-evm' +import { first } from 'lodash-unified' +import { Explorer } from '@masknet/web3-providers' +import { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' +import { ChainId, getExplorerConstants } from '@masknet/web3-shared-evm' export class AccountChecker implements TransactionChecker { - checkStatus(chainId: ChainId, id: string): Promise { - console.log('DEBUG: account checker') - console.log({ - chainId, - id, + static CHECK_TIMES = 30 + static CHECK_DELAY = 30 * 1000 // seconds + static CHECK_LATEST_SIZE = 5 + + async checkStatus(id: string, chainId: ChainId, account: string): Promise { + const { API_KEYS = [], EXPLORER_API = '' } = getExplorerConstants(chainId) + const latestTransactions = await Explorer.getLatestTransactions(account, EXPLORER_API, { + offset: AccountChecker.CHECK_LATEST_SIZE, + apikey: first(API_KEYS), }) - throw new Error('Method not implemented.') + const tx = latestTransactions.find((x) => x.hash === id) + if (!tx) return TransactionStatusType.NOT_DEPEND + return tx.status === '0' ? TransactionStatusType.SUCCEED : TransactionStatusType.FAILED } } diff --git a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts index c34677653a24..3b387f24c374 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts @@ -4,7 +4,7 @@ import { Web3StateSettings } from '../../../settings' import { getReceiptStatus } from '../../Connection/utils' export class ReceiptChecker implements TransactionChecker { - async checkStatus(chainId: ChainId, id: string): Promise { + async checkStatus(id: string, chainId: ChainId): Promise { const connection = await Web3StateSettings.value.Connection?.getConnection?.({ chainId, }) diff --git a/packages/shared-base/src/utils/subscription.ts b/packages/shared-base/src/utils/subscription.ts index 428e83836470..7ee9dc450792 100644 --- a/packages/shared-base/src/utils/subscription.ts +++ b/packages/shared-base/src/utils/subscription.ts @@ -1,7 +1,7 @@ -import { noop, reject } from 'lodash-unified' -import type { ValueRef } from '@dimensiondev/holoflows-kit' +import { noop } from 'lodash-unified' import type { Subscription } from 'use-subscription' import { None, Option, Some } from 'ts-results' +import type { ValueRef } from '@dimensiondev/holoflows-kit' export async function getSubscriptionCurrentValue( getSubscription: () => Subscription | undefined, diff --git a/packages/web3-shared/base/src/specs/index.ts b/packages/web3-shared/base/src/specs/index.ts index b1cc39d155c0..b8d8700803c8 100644 --- a/packages/web3-shared/base/src/specs/index.ts +++ b/packages/web3-shared/base/src/specs/index.ts @@ -556,7 +556,7 @@ export interface ProviderOptions { } export interface TransactionChecker { - checkStatus(chainId: ChainId, id: string): Promise + checkStatus(id: string, chainId: ChainId, account: string): Promise } export interface ConnectionOptions { From 6aaa17ee544ea61799b9f58b90a9516511970e04 Mon Sep 17 00:00:00 2001 From: guanbinrui Date: Mon, 13 Jun 2022 10:14:15 +0800 Subject: [PATCH 3/6] refactor: code style --- .../src/web3-state/TransactionWatcher.ts | 23 +++++++++++-------- .../EVM/src/state/TransactionWatcher.ts | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts index c6b5ed80e8e6..b2a4081e55b5 100644 --- a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts +++ b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts @@ -10,7 +10,7 @@ import { } from '@masknet/web3-shared-base' import type { Plugin } from '../types' -export interface TransactionWatcherItem { +interface TransactionWatcherItem { at: number id: string chainId: ChainId @@ -18,7 +18,7 @@ export interface TransactionWatcherItem { transaction: Transaction } -export type TransactionWatcher = Record< +type TransactionWatcher = Record< // @ts-ignore ChainId, Record< @@ -29,7 +29,6 @@ export type TransactionWatcher = Record< > class Watcher { - static LATEST_TRANSACTION_SIZE = 5 static MAX_ITEM_SIZE = 40 private timer: NodeJS.Timeout | null = null @@ -38,8 +37,8 @@ class Watcher { protected storage: StorageItem>, protected checkers: Array>, protected options: { - delay: number - getCreatorAddress: (transaction: Transaction) => string + checkDelay: number + getTransactionCreator: (transaction: Transaction) => string onNotify: (id: string, status: TransactionStatusType, transaction: Transaction) => void }, ) {} @@ -104,7 +103,11 @@ class Watcher { for (const [id, { transaction }] of watchedTransactions) { for (const checker of this.checkers) { try { - const status = await checker.checkStatus(id, chainId, this.options.getCreatorAddress(transaction)) + const status = await checker.checkStatus( + id, + chainId, + this.options.getTransactionCreator(transaction), + ) if (status !== TransactionStatusType.NOT_DEPEND) { this.removeTransaction(chainId, id) this.options.onNotify(id, status, transaction) @@ -123,7 +126,7 @@ class Watcher { public startCheck(chainId: ChainId) { this.stopCheck() if (this.timer === null) { - this.timer = setTimeout(this.check.bind(this, chainId), this.options.delay) + this.timer = setTimeout(this.check.bind(this, chainId), this.options.checkDelay) } } @@ -167,7 +170,7 @@ export class TransactionWatcherState /** Default block delay in seconds */ defaultBlockDelay: number /** Get the author address */ - getCreatorAddress: (transaction: Transaction) => string + getTransactionCreator: (transaction: Transaction) => string }, ) { const defaultValue = Object.fromEntries(chainIds.map((x) => [x, {}])) as TransactionWatcher< @@ -203,9 +206,9 @@ export class TransactionWatcherState this.watchers.set( chainId, new Watcher(this.storage, this.checkers, { - delay: this.options.defaultBlockDelay * 1000, + checkDelay: this.options.defaultBlockDelay * 1000, + getTransactionCreator: this.options.getTransactionCreator, onNotify: this.notifyTransaction.bind(this), - getCreatorAddress: this.options.getCreatorAddress, }), ) return this.watchers.get(chainId)! diff --git a/packages/plugins/EVM/src/state/TransactionWatcher.ts b/packages/plugins/EVM/src/state/TransactionWatcher.ts index 991f6e99f6ce..8f7fe8f78c40 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher.ts @@ -19,7 +19,7 @@ export class TransactionWatcher extends TransactionWatcherState (tx.from as string | undefined) ?? '', + getTransactionCreator: (tx) => (tx.from as string | undefined) ?? '', }, ) } From a6d739d6ea05d4a4bd64c3e1fab433ce64ce6cad Mon Sep 17 00:00:00 2001 From: guanbinrui Date: Mon, 13 Jun 2022 11:06:35 +0800 Subject: [PATCH 4/6] fix: gitcoin donation bugs --- .../mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx | 7 +++++-- .../mask/src/plugins/Gitcoin/hooks/useDonateCallback.ts | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx index 4784898a1bae..5111b5b48bed 100644 --- a/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx +++ b/packages/mask/src/plugins/Gitcoin/SNSAdaptor/DonateDialog.tsx @@ -75,7 +75,7 @@ export function DonateDialog(props: DonateDialogProps) { nativeTokenDetailed.value, ) - const tokenBalance = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM, nativeTokenDetailed.value?.address) + const tokenBalance = useFungibleTokenBalance(NetworkPluginID.PLUGIN_EVM, token?.address) // #region select token dialog const pickToken = usePickToken() @@ -123,6 +123,9 @@ export function DonateDialog(props: DonateDialogProps) { activatedSocialNetworkUI.utils.share?.(shareText) }, }) + + // clean dialog + setRawAmount('') }, [openShareTxDialog, token, donateCallback, tr, t]) // #region submit button @@ -160,7 +163,7 @@ export function DonateDialog(props: DonateDialogProps) { }} /> - + , diff --git a/packages/mask/src/plugins/Gitcoin/hooks/useDonateCallback.ts b/packages/mask/src/plugins/Gitcoin/hooks/useDonateCallback.ts index e507a0358af3..4c600061d98f 100644 --- a/packages/mask/src/plugins/Gitcoin/hooks/useDonateCallback.ts +++ b/packages/mask/src/plugins/Gitcoin/hooks/useDonateCallback.ts @@ -28,12 +28,12 @@ export function useDonateCallback(address: string, amount: string, token?: Fungi return [ [ token.schema === SchemaType.Native ? GITCOIN_ETH_ADDRESS : token.address, // token - tipAmount.toFixed(), // amount + tipAmount.toFixed(0), // amount address, // dest ], [ token.schema === SchemaType.Native ? GITCOIN_ETH_ADDRESS : token.address, // token - grantAmount.toFixed(), // amount + grantAmount.toFixed(0), // amount address, // dest ], ] From 570cdf5c446255ebb6e8289c2ecc63c2864a8a06 Mon Sep 17 00:00:00 2001 From: guanbinrui Date: Mon, 13 Jun 2022 11:30:49 +0800 Subject: [PATCH 5/6] refactor: code style --- .../src/web3-state/TransactionWatcher.ts | 19 +++++++++++++++++ .../EVM/src/state/TransactionWatcher.ts | 2 +- .../checkers/AccountChecker.ts | 21 ++++++++++++------- .../checkers/ReceiptChecker.ts | 4 ++-- .../shared-base/src/utils/subscription.ts | 3 ++- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts index b2a4081e55b5..ba96aee2e89a 100644 --- a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts +++ b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts @@ -7,6 +7,7 @@ import { TransactionStatusType, WatchEvents, TransactionWatcherState as Web3TransactionWatcherState, + RecentTransaction, } from '@masknet/web3-shared-base' import type { Plugin } from '../types' @@ -165,6 +166,7 @@ export class TransactionWatcherState protected checkers: Array>, protected subscriptions: { chainId?: Subscription + transactions?: Subscription>> }, protected options: { /** Default block delay in seconds */ @@ -183,6 +185,7 @@ export class TransactionWatcherState this.storage = storage.value + // kick watcher to start work if (this.subscriptions.chainId) { const resume = () => { const chainId = this.subscriptions.chainId?.getCurrentValue() @@ -199,6 +202,22 @@ export class TransactionWatcherState resume() }) } + + // add external transactions at startup + if (this.subscriptions.transactions) { + const resume = () => { + const transactions = this.subscriptions.transactions?.getCurrentValue() + transactions?.forEach((x) => + Object.entries(x.candidates).forEach(([id, tx]) => this.watchTransaction(x.chainId, id, tx)), + ) + } + + getSubscriptionCurrentValue(() => { + return this.subscriptions.transactions + }).then(() => { + resume() + }) + } } private getWatcher(chainId: ChainId) { diff --git a/packages/plugins/EVM/src/state/TransactionWatcher.ts b/packages/plugins/EVM/src/state/TransactionWatcher.ts index 8f7fe8f78c40..a66130bb5a3d 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher.ts @@ -1,8 +1,8 @@ +import type { Subscription } from 'use-subscription' import { getEnumAsArray } from '@dimensiondev/kit' import type { Plugin } from '@masknet/plugin-infra' import { TransactionWatcherState } from '@masknet/plugin-infra/web3' import { ChainId, Transaction } from '@masknet/web3-shared-evm' -import type { Subscription } from 'use-subscription' import { TransactionCheckers } from './TransactionWatcher/checker' export class TransactionWatcher extends TransactionWatcherState { diff --git a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts index 64c7556fb47d..cf4229bf2d21 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/AccountChecker.ts @@ -3,19 +3,26 @@ import { Explorer } from '@masknet/web3-providers' import { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' import { ChainId, getExplorerConstants } from '@masknet/web3-shared-evm' +/** + * Fetch latest tx list of the the given account. It creates a signature for each transaction. + * Treat two transactions the same with the identical transaction hash or signature. + */ export class AccountChecker implements TransactionChecker { - static CHECK_TIMES = 30 - static CHECK_DELAY = 30 * 1000 // seconds - static CHECK_LATEST_SIZE = 5 + static CHECK_LATEST_TRANSACTION_SIZE = 5 - async checkStatus(id: string, chainId: ChainId, account: string): Promise { + private async fetchLatestTransactions(chainId: ChainId, account: string) { const { API_KEYS = [], EXPLORER_API = '' } = getExplorerConstants(chainId) - const latestTransactions = await Explorer.getLatestTransactions(account, EXPLORER_API, { - offset: AccountChecker.CHECK_LATEST_SIZE, + return Explorer.getLatestTransactions(account, EXPLORER_API, { + offset: AccountChecker.CHECK_LATEST_TRANSACTION_SIZE, apikey: first(API_KEYS), }) + } + + async checkStatus(id: string, chainId: ChainId, account: string): Promise { + const latestTransactions = await this.fetchLatestTransactions(chainId, account) const tx = latestTransactions.find((x) => x.hash === id) if (!tx) return TransactionStatusType.NOT_DEPEND - return tx.status === '0' ? TransactionStatusType.SUCCEED : TransactionStatusType.FAILED + // '1' for successful transactions and '0' for failed transactions. + return tx.status === '1' ? TransactionStatusType.SUCCEED : TransactionStatusType.FAILED } } diff --git a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts index 3b387f24c374..e3fe518a7195 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher/checkers/ReceiptChecker.ts @@ -1,4 +1,4 @@ -import { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' +import type { TransactionChecker, TransactionStatusType } from '@masknet/web3-shared-base' import type { ChainId } from '@masknet/web3-shared-evm' import { Web3StateSettings } from '../../../settings' import { getReceiptStatus } from '../../Connection/utils' @@ -9,6 +9,6 @@ export class ReceiptChecker implements TransactionChecker { chainId, }) const receipt = await connection?.getTransactionReceipt(id) - return getReceiptStatus(receipt ?? null) ?? TransactionStatusType.NOT_DEPEND + return getReceiptStatus(receipt ?? null) } } diff --git a/packages/shared-base/src/utils/subscription.ts b/packages/shared-base/src/utils/subscription.ts index 7ee9dc450792..e5a2cf188d83 100644 --- a/packages/shared-base/src/utils/subscription.ts +++ b/packages/shared-base/src/utils/subscription.ts @@ -5,6 +5,7 @@ import type { ValueRef } from '@dimensiondev/holoflows-kit' export async function getSubscriptionCurrentValue( getSubscription: () => Subscription | undefined, + retries = 3, ): Promise { const getValue = () => { return getSubscription()?.getCurrentValue() @@ -20,7 +21,7 @@ export async function getSubscriptionCurrentValue( } } - const createReaders = Array.from<() => Promise>({ length: 3 }).fill(() => createReader()) + const createReaders = Array.from<() => Promise>({ length: retries }).fill(() => createReader()) for (const createReader of createReaders) { try { From 2d4a6b3c4c0444baf94bb2914dd9ddeb3fc311f4 Mon Sep 17 00:00:00 2001 From: guanbinrui Date: Mon, 13 Jun 2022 16:33:08 +0800 Subject: [PATCH 6/6] fix: handle transactions --- .../src/web3-state/TransactionWatcher.ts | 70 ++++++++++--------- .../src/web3/useClearTransactionsCallback.ts | 6 +- .../src/web3/useRemoveTransaction.ts | 4 +- .../Connection/middleware/Transaction.ts | 2 +- .../EVM/src/state/TransactionWatcher.ts | 2 + packages/plugins/EVM/src/state/index.ts | 10 +-- packages/web3-shared/base/src/specs/index.ts | 4 +- 7 files changed, 53 insertions(+), 45 deletions(-) diff --git a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts index ba96aee2e89a..e58a25ecef44 100644 --- a/packages/plugin-infra/src/web3-state/TransactionWatcher.ts +++ b/packages/plugin-infra/src/web3-state/TransactionWatcher.ts @@ -48,8 +48,8 @@ class Watcher { return this.storage.value[chainId] } - private setStorage(chainId: ChainId, id: string, item: TransactionWatcherItem) { - this.storage.setValue({ + private async setStorage(chainId: ChainId, id: string, item: TransactionWatcherItem) { + await this.storage.setValue({ ...this.storage.value, // @ts-ignore [chainId]: { @@ -59,20 +59,24 @@ class Watcher { }) } - private deleteStorage(chainId: ChainId, id: string) { - this.storage.setValue({ + private async deleteStorage(chainId: ChainId, id: string) { + await this.storage.setValue({ ...this.storage.value, // @ts-ignore [chainId]: omit(this.storage.value[chainId], [id]), }) } - private setTransaction(chainId: ChainId, id: string, transaction: TransactionWatcherItem) { - this.setStorage(chainId, id, transaction) + private async setTransaction( + chainId: ChainId, + id: string, + transaction: TransactionWatcherItem, + ) { + await this.setStorage(chainId, id, transaction) } - private removeTransaction(chainId: ChainId, id: string) { - this.deleteStorage(chainId, id) + private async removeTransaction(chainId: ChainId, id: string) { + await this.deleteStorage(chainId, id) } private getAllTransactions(chainId: ChainId) { @@ -93,7 +97,9 @@ class Watcher { this.stopCheck() // unwatch legacy transactions - this.getUnwatched(chainId).forEach(([id]) => this.unwatchTransaction(chainId, id)) + for (const [id] of this.getUnwatched(chainId)) { + await this.unwatchTransaction(chainId, id) + } // check if all transactions were sealed const watchedTransactions = this.getWatched(chainId).filter( @@ -114,8 +120,7 @@ class Watcher { this.options.onNotify(id, status, transaction) } } catch (error) { - console.log('DEBUG: check error') - console.log(error) + console.warn('Failed to check transaction status.') } } } @@ -136,8 +141,8 @@ class Watcher { this.timer = null } - public watchTransaction(chainId: ChainId, id: string, transaction: Transaction) { - this.setTransaction(chainId, id, { + public async watchTransaction(chainId: ChainId, id: string, transaction: Transaction) { + await this.setTransaction(chainId, id, { at: Date.now(), id, chainId, @@ -147,8 +152,8 @@ class Watcher { this.startCheck(chainId) } - public unwatchTransaction(chainId: ChainId, id: string) { - this.removeTransaction(chainId, id) + public async unwatchTransaction(chainId: ChainId, id: string) { + await this.removeTransaction(chainId, id) } } @@ -185,17 +190,23 @@ export class TransactionWatcherState this.storage = storage.value - // kick watcher to start work - if (this.subscriptions.chainId) { - const resume = () => { - const chainId = this.subscriptions.chainId?.getCurrentValue() - if (chainId) this.resumeWatcher(chainId) + const resume = async () => { + const chainId = this.subscriptions.chainId?.getCurrentValue() + if (chainId) this.resumeWatcher(chainId) + + const transactions = this.subscriptions.transactions?.getCurrentValue() ?? [] + for (const transaction of transactions) { + for (const [id, tx] of Object.entries(transaction.candidates)) { + if (transaction.status === TransactionStatusType.NOT_DEPEND) + await this.watchTransaction(transaction.chainId, id, tx) + } } + } - // resume watcher if chain id changed + // resume watcher if chain id changed + if (this.subscriptions.chainId) { this.subscriptions.chainId.subscribe(() => resume()) - // storage has set up, resume watcher getSubscriptionCurrentValue(() => { return this.subscriptions.chainId }).then(() => { @@ -205,13 +216,6 @@ export class TransactionWatcherState // add external transactions at startup if (this.subscriptions.transactions) { - const resume = () => { - const transactions = this.subscriptions.transactions?.getCurrentValue() - transactions?.forEach((x) => - Object.entries(x.candidates).forEach(([id, tx]) => this.watchTransaction(x.chainId, id, tx)), - ) - } - getSubscriptionCurrentValue(() => { return this.subscriptions.transactions }).then(() => { @@ -238,13 +242,13 @@ export class TransactionWatcherState watcher.startCheck(chainId) } - watchTransaction(chainId: ChainId, id: string, transaction: Transaction) { - this.getWatcher(chainId).watchTransaction(chainId, id, transaction) + async watchTransaction(chainId: ChainId, id: string, transaction: Transaction) { + await this.getWatcher(chainId).watchTransaction(chainId, id, transaction) this.emitter.emit('progress', id, TransactionStatusType.NOT_DEPEND, transaction) } - unwatchTransaction(chainId: ChainId, id: string) { - this.getWatcher(chainId).unwatchTransaction(chainId, id) + async unwatchTransaction(chainId: ChainId, id: string) { + await this.getWatcher(chainId).unwatchTransaction(chainId, id) } notifyTransaction(id: string, status: TransactionStatusType, transaction: Transaction) { diff --git a/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts b/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts index b8b5d1c26c9e..7261c8880d10 100644 --- a/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts +++ b/packages/plugin-infra/src/web3/useClearTransactionsCallback.ts @@ -15,9 +15,9 @@ export function useClearTransactionsCallback(pluginID try { const transactions = await getSubscriptionCurrentValue(() => Transaction?.transactions) - transactions - ?.flatMap((x) => Object.keys(x.candidates)) - .forEach((x) => TransactionWatcher?.unwatchTransaction(chainId, x)) + for (const transaction of transactions?.flatMap((x) => Object.keys(x.candidates)) ?? []) { + await TransactionWatcher?.unwatchTransaction(chainId, transaction) + } } catch { console.warn('Failed to unwatch transaction.') } diff --git a/packages/plugin-infra/src/web3/useRemoveTransaction.ts b/packages/plugin-infra/src/web3/useRemoveTransaction.ts index 4d19852cbcb7..49326449409d 100644 --- a/packages/plugin-infra/src/web3/useRemoveTransaction.ts +++ b/packages/plugin-infra/src/web3/useRemoveTransaction.ts @@ -12,8 +12,8 @@ export function useRemoveTransactionCallback(pluginID return useCallback( async (id: string) => { if (!account) return - TransactionWatcher?.unwatchTransaction(chainId, id) - return Transaction?.removeTransaction?.(chainId, account, id) + await TransactionWatcher?.unwatchTransaction(chainId, id) + await Transaction?.removeTransaction?.(chainId, account, id) }, [chainId, account, Transaction], ) diff --git a/packages/plugins/EVM/src/state/Connection/middleware/Transaction.ts b/packages/plugins/EVM/src/state/Connection/middleware/Transaction.ts index f9281ef74731..2819826ff527 100644 --- a/packages/plugins/EVM/src/state/Connection/middleware/Transaction.ts +++ b/packages/plugins/EVM/src/state/Connection/middleware/Transaction.ts @@ -22,7 +22,7 @@ export class RecentTransaction implements Middleware { context.result, context.config, ) - TransactionWatcher?.watchTransaction(context.chainId, context.result, context.config) + await TransactionWatcher?.watchTransaction(context.chainId, context.result, context.config) break case EthereumMethodType.ETH_GET_TRANSACTION_RECEIPT: if (isSquashed) return diff --git a/packages/plugins/EVM/src/state/TransactionWatcher.ts b/packages/plugins/EVM/src/state/TransactionWatcher.ts index a66130bb5a3d..5466bddad638 100644 --- a/packages/plugins/EVM/src/state/TransactionWatcher.ts +++ b/packages/plugins/EVM/src/state/TransactionWatcher.ts @@ -3,6 +3,7 @@ import { getEnumAsArray } from '@dimensiondev/kit' import type { Plugin } from '@masknet/plugin-infra' import { TransactionWatcherState } from '@masknet/plugin-infra/web3' import { ChainId, Transaction } from '@masknet/web3-shared-evm' +import type { RecentTransaction } from '@masknet/web3-shared-base' import { TransactionCheckers } from './TransactionWatcher/checker' export class TransactionWatcher extends TransactionWatcherState { @@ -10,6 +11,7 @@ export class TransactionWatcher extends TransactionWatcherState + transactions?: Subscription>> }, ) { super( diff --git a/packages/plugins/EVM/src/state/index.ts b/packages/plugins/EVM/src/state/index.ts index d0b158b09be0..e40147fa2eee 100644 --- a/packages/plugins/EVM/src/state/index.ts +++ b/packages/plugins/EVM/src/state/index.ts @@ -20,6 +20,10 @@ import { BlockNumberNotifier } from './BlockNumberNotifier' export function createWeb3State(context: Plugin.Shared.SharedContext): EVM_Web3State { const Provider_ = new Provider(context) const Settings_ = new Settings(context) + const Transaction_ = new Transaction(context, { + chainId: Provider_.chainId, + account: Provider_.account, + }) return { Settings: Settings_, @@ -44,13 +48,11 @@ export function createWeb3State(context: Plugin.Shared.SharedContext): EVM_Web3S Token: new Token(context, { account: Provider_.account, }), - Transaction: new Transaction(context, { - chainId: Provider_.chainId, - account: Provider_.account, - }), + Transaction: Transaction_, TransactionFormatter: new TransactionFormatter(context), TransactionWatcher: new TransactionWatcher(context, { chainId: Provider_.chainId, + transactions: Transaction_.transactions, }), Connection: new Connection(context, { chainId: Provider_.chainId, diff --git a/packages/web3-shared/base/src/specs/index.ts b/packages/web3-shared/base/src/specs/index.ts index b8d8700803c8..bccc344cfa85 100644 --- a/packages/web3-shared/base/src/specs/index.ts +++ b/packages/web3-shared/base/src/specs/index.ts @@ -927,9 +927,9 @@ export interface TransactionWatcherState { emitter: Emitter> /** Add a transaction into the watch list. */ - watchTransaction: (chainId: ChainId, id: string, transaction: Transaction) => void + watchTransaction: (chainId: ChainId, id: string, transaction: Transaction) => Promise /** Remove a transaction from the watch list. */ - unwatchTransaction: (chainId: ChainId, id: string) => void + unwatchTransaction: (chainId: ChainId, id: string) => Promise /** Update transaction status */ notifyTransaction: (id: string, status: TransactionStatusType, transaction: Transaction) => void }