Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add balance & blocknumber notifier #6489

Merged
merged 4 commits into from
Jun 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/mask/background/services/helper/r2d2Fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ export async function r2d2Fetch(input: RequestInfo, init?: RequestInit): Promise

// r2d2 worker
const r2deWorkerType = matchers.find((x) => url.startsWith(x[0]))?.[1]
if (r2deWorkerType) globalThis.fetch(url.replace(new URL(url).origin, `https://${r2deWorkerType}.${r2d2URL}`), init)
if (r2deWorkerType)
return globalThis.fetch(url.replace(new URL(url).origin, `https://${r2deWorkerType}.${r2d2URL}`), init)

// fallback
return globalThis.fetch(input, init)
Expand Down
2 changes: 1 addition & 1 deletion packages/mask/src/plugins/Gitcoin/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export const PLUGIN_ID = PluginId.Gitcoin
export const PLUGIN_META_KEY = `${PluginId.Gitcoin}:1`
export const PLUGIN_NAME = 'Gitcoin'
export const PLUGIN_DESCRIPTION = 'Gitcoin grants sustain web3 projects with quadratic funding.'
export const GITCOIN_API_GRANTS_V1 = 'https://gitcoin.co/grants/v1/api/grant/:id'
export const GITCOIN_API_GRANTS_V1 = 'https://gitcoin.co/grants/v1/api/grant/:id/'
3 changes: 3 additions & 0 deletions packages/mask/src/web3/UI/TokenAmountPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ export function TokenAmountPanel(props: TokenAmountPanelProps) {
type="text"
value={amount}
variant="outlined"
onKeyDown={(ev) => {
if (ev.key === 'Enter') ev.preventDefault()
}}
onChange={onChange}
placeholder="0.0"
InputProps={{
Expand Down
6 changes: 6 additions & 0 deletions packages/plugin-infra/src/web3-state/BalanceNotifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Emitter } from '@servie/events'
import type { BalanceEvent, BalanceNotifierState as Web3BalanceNotifierState } from '@masknet/web3-shared-base'

export class BalanceNotifierState<ChainId> implements Web3BalanceNotifierState<ChainId> {
emitter: Emitter<BalanceEvent<ChainId>> = new Emitter()
}
9 changes: 9 additions & 0 deletions packages/plugin-infra/src/web3-state/BlockNumberNotifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Emitter } from '@servie/events'
import type {
BlockNumberEvent,
BlockNumberNotifierState as Web3BlockNumberNotifierState,
} from '@masknet/web3-shared-base'

export class BlockNumberNotifierState<ChainId> implements Web3BlockNumberNotifierState<ChainId> {
emitter: Emitter<BlockNumberEvent<ChainId>> = new Emitter()
}
2 changes: 2 additions & 0 deletions packages/plugin-infra/src/web3-state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export * from './Provider'
export * from './Identity'
export * from './Wallet'
export * from './Others'
export * from './BalanceNotifier'
export * from './BlockNumberNotifier'
4 changes: 4 additions & 0 deletions packages/plugin-infra/src/web3-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type {
BalanceNotifierState,
BlockNumberNotifierState,
NetworkDescriptor,
ProviderDescriptor,
AddressBookState,
Expand Down Expand Up @@ -37,6 +39,8 @@ export declare namespace Web3Plugin {
Web3Provider,
> {
AddressBook?: AddressBookState<ChainId>
BalanceNotifier?: BalanceNotifierState<ChainId>
BlockNumberNotifier?: BlockNumberNotifierState<ChainId>
Hub?: HubState<ChainId, SchemaType, GasOption>
IdentityService?: IdentityServiceState
NameService?: NameServiceState<ChainId>
Expand Down
20 changes: 18 additions & 2 deletions packages/plugin-infra/src/web3/useBalance.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
import { useEffect } from 'react'
import { useAsyncRetry } from 'react-use'
import { noop } from 'lodash-unified'
import type { NetworkPluginID } from '@masknet/web3-shared-base'
import type { Web3Helper } from '../web3-helpers'
import { useAccount } from './useAccount'
import { useChainId } from './useChainId'
import { useWeb3Connection } from './useWeb3Connection'
import { useWeb3State } from '../entry-web3'

export function useBalance<S extends 'all' | void = void, T extends NetworkPluginID = NetworkPluginID>(
pluginID?: T,
options?: Web3Helper.Web3ConnectionOptionsScope<S, T>,
) {
const account = useAccount(pluginID, options?.account)
const chainId = useChainId(pluginID, options?.chainId)
const connection = useWeb3Connection(pluginID, options)
const { BalanceNotifier, Others } = useWeb3State(pluginID)

return useAsyncRetry(async () => {
const asyncResult = useAsyncRetry(async () => {
if (!account || !connection) return '0'
return connection.getBalance(account)
}, [account, connection])
}, [account, chainId, connection])

useEffect(() => {
return (
BalanceNotifier?.emitter.on('update', (ev) => {
if (Others?.isSameAddress(account, ev.account)) asyncResult.retry()
}) ?? noop
)
}, [account, asyncResult.retry, BalanceNotifier, Others])

return asyncResult
}
16 changes: 15 additions & 1 deletion packages/plugin-infra/src/web3/useBlockNumber.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
import { useEffect } from 'react'
import { useAsyncRetry } from 'react-use'
import { noop } from 'lodash-unified'
import type { NetworkPluginID } from '@masknet/web3-shared-base'
import type { Web3Helper } from '../web3-helpers'
import { useChainId } from './useChainId'
import { useWeb3Connection } from './useWeb3Connection'
import { useWeb3State } from '../entry-web3'

export function useBlockNumber<S extends 'all' | void = void, T extends NetworkPluginID = NetworkPluginID>(
pluginID?: T,
options?: Web3Helper.Web3ConnectionOptionsScope<S, T>,
) {
const chainId = useChainId(pluginID, options?.chainId)
const connection = useWeb3Connection(pluginID, options)
const { BlockNumberNotifier } = useWeb3State(pluginID)

return useAsyncRetry(async () => {
const asyncRetry = useAsyncRetry(async () => {
if (!connection) return 0
return connection.getBlockNumber()
}, [chainId, connection])

useEffect(() => {
return (
BlockNumberNotifier?.emitter.on('update', (actualChainId) => {
if (actualChainId === chainId) asyncRetry.retry()
}) ?? noop
)
}, [chainId, asyncRetry.retry, BlockNumberNotifier])

return asyncRetry
}
28 changes: 24 additions & 4 deletions packages/plugin-infra/src/web3/useFungibleTokenBalance.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
import type { NetworkPluginID } from '@masknet/web3-shared-base'
import { useEffect } from 'react'
import useAsyncRetry from 'react-use/lib/useAsyncRetry'
import { noop } from 'lodash-unified'
import type { NetworkPluginID } from '@masknet/web3-shared-base'
import { useWeb3State } from '../entry-web3'
import type { Web3Helper } from '../web3-helpers'
import { useAccount } from './useAccount'
import { useChainId } from './useChainId'
import { useWeb3Connection } from './useWeb3Connection'

export function useFungibleTokenBalance<S extends 'all' | void = void, T extends NetworkPluginID = NetworkPluginID>(
pluginID?: T,
address?: string,
options?: Web3Helper.Web3ConnectionOptionsScope<S, T>,
) {
const account = useAccount(pluginID, options?.account)
const chainId = useChainId(pluginID, options?.chainId)
const connection = useWeb3Connection(pluginID, options)
const { BalanceNotifier, Others } = useWeb3State(pluginID)

return useAsyncRetry(async () => {
if (!connection || !address) return '0'
const asyncRetry = useAsyncRetry(async () => {
if (!connection) return '0'
return connection.getFungibleTokenBalance(address ?? '', options)
}, [address, connection, JSON.stringify(options)])
}, [account, address, chainId, connection, JSON.stringify(options)])

useEffect(() => {
return (
BalanceNotifier?.emitter.on('update', (ev) => {
if (Others?.isSameAddress(account, ev.account)) {
asyncRetry.retry()
}
}) ?? noop
)
}, [account, asyncRetry.retry, BalanceNotifier, Others])

return asyncRetry
}
8 changes: 8 additions & 0 deletions packages/plugins/EVM/src/state/BalanceNotifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BalanceNotifierState } from '@masknet/plugin-infra/web3'
import type { ChainId } from '@masknet/web3-shared-evm'

export class BalanceNotifier extends BalanceNotifierState<ChainId> {
constructor() {
super()
}
}
8 changes: 8 additions & 0 deletions packages/plugins/EVM/src/state/BlockNumberNotifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { BlockNumberNotifierState } from '@masknet/plugin-infra/web3'
import type { ChainId } from '@masknet/web3-shared-evm'

export class BlockNumberNotifier extends BlockNumberNotifierState<ChainId> {
constructor() {
super()
}
}
7 changes: 4 additions & 3 deletions packages/plugins/EVM/src/state/Connection/connection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Web3 from 'web3'
import { AbiItem, numberToHex, toHex, toNumber } from 'web3-utils'
import { first } from 'lodash-unified'
import urlcat from 'urlcat'
Expand Down Expand Up @@ -30,8 +29,10 @@ import {
sendTransaction,
createERC20Token,
parseStringOrBytes32,
createWeb3,
createWeb3Provider,
getEthereumConstants,
isValidAddress,
} from '@masknet/web3-shared-evm'
import {
Account,
Expand Down Expand Up @@ -161,7 +162,7 @@ class Connection implements EVM_Connection {
}

getWeb3(options?: EVM_Web3ConnectionOptions) {
const web3 = new Web3(
const web3 = createWeb3(
createWeb3Provider((requestArguments: RequestArguments) => this.hijackedRequest(requestArguments, options)),
)
return Promise.resolve(web3)
Expand Down Expand Up @@ -464,7 +465,7 @@ class Connection implements EVM_Connection {
}
async getNativeTokenBalance(options?: EVM_Web3ConnectionOptions): Promise<string> {
const account = options?.account ?? this.account
if (!account) return '0'
if (!isValidAddress(account)) return '0'
return this.getBalance(options?.account ?? this.account, options)
}
async getFungibleTokenBalance(address: string, options?: EVM_Web3ConnectionOptions): Promise<string> {
Expand Down
13 changes: 12 additions & 1 deletion packages/plugins/EVM/src/state/Connection/middleware/Squash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ export class Squash implements Middleware<Context> {
const [address, tag = 'latest'] = params as [string, string]
return sha3([chainId, method, address, tag].join())
}
case EthereumMethodType.ETH_GET_BLOCK_BY_NUMBER: {
const [number, full] = params as [string, boolean]
return sha3([chainId, method, number, full].join())
}
case EthereumMethodType.ETH_BLOCK_NUMBER: {
return sha3([chainId, method].join())
}
case EthereumMethodType.ETH_GET_BALANCE:
case EthereumMethodType.ETH_GET_TRANSACTION_COUNT:
const [account, tag = 'latest'] = params as [string, string]
Expand Down Expand Up @@ -70,7 +77,11 @@ export class Squash implements Middleware<Context> {

this.cache.set(id, promise)
await next()
this.cache.delete(id)

// keep cache for 1s
setTimeout(() => {
this.cache.delete(id)
}, 1000)

if (context.error) reject(context.error)
else resolve(context.result)
Expand Down
47 changes: 31 additions & 16 deletions packages/plugins/EVM/src/state/Connection/middleware/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,49 @@ import { Web3StateSettings } from '../../../settings'

export class RecentTransaction implements Middleware<Context> {
async fn(context: Context, next: () => Promise<void>) {
const { Transaction, TransactionWatcher } = Web3StateSettings.value
const { Transaction, TransactionWatcher, BalanceNotifier, BlockNumberNotifier } = Web3StateSettings.value
const isSquashed = typeof context.result !== 'undefined'

await next()

try {
switch (context.method) {
case EthereumMethodType.ETH_SEND_TRANSACTION:
if (!context.config || typeof context.result !== 'string') return
else
await Transaction?.addTransaction?.(
context.chainId,
context.account,
context.result,
context.config,
)
await Transaction?.addTransaction?.(
context.chainId,
context.account,
context.result,
context.config,
)
TransactionWatcher?.watchTransaction(context.chainId, context.result, context.config)
break
case EthereumMethodType.ETH_GET_TRANSACTION_RECEIPT:
if (isSquashed) return

const receipt = context.result as TransactionReceipt | null
const status = getReceiptStatus(receipt)
if (receipt?.transactionHash && status !== TransactionStatusType.NOT_DEPEND) {
await Transaction?.updateTransaction?.(
context.chainId,
context.account,
receipt.transactionHash,
status,
)
}
if (!receipt?.transactionHash || status === TransactionStatusType.NOT_DEPEND) return

// update in house transaction state
await Transaction?.updateTransaction?.(
context.chainId,
context.account,
receipt.transactionHash,
status,
)

// update built-in notifier
BalanceNotifier?.emitter.emit('update', {
chainId: context.chainId,
account: receipt.from,
})
// it could be a contract address, but it doesn't matter
BalanceNotifier?.emitter.emit('update', {
chainId: context.chainId,
account: receipt.to,
})
BlockNumberNotifier?.emitter.emit('update', context.chainId)
break
case EthereumMethodType.MASK_REPLACE_TRANSACTION:
if (!context.config || typeof context.result !== 'string') return
Expand Down
7 changes: 3 additions & 4 deletions packages/plugins/EVM/src/state/Connection/providers/Base.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Web3 from 'web3'
import { toHex } from 'web3-utils'
import type { RequestArguments } from 'web3-core'
import { delay } from '@dimensiondev/kit'
import { Emitter } from '@servie/events'
import type { Account, ProviderEvents, ProviderOptions } from '@masknet/web3-shared-base'
import {
chainResolver,
createWeb3,
createWeb3Provider,
ChainId,
ProviderType,
Expand All @@ -13,7 +14,6 @@ import {
NetworkType,
} from '@masknet/web3-shared-evm'
import type { EVM_Provider } from '../types'
import { delay } from '@dimensiondev/kit'

export class BaseProvider implements EVM_Provider {
emitter = new Emitter<ProviderEvents<ChainId, ProviderType>>()
Expand Down Expand Up @@ -79,8 +79,7 @@ export class BaseProvider implements EVM_Provider {

// Create a web3 instance from the external provider by default.
async createWeb3(options?: ProviderOptions<ChainId>) {
// @ts-ignore
return new Web3(await this.createWeb3Provider(options))
return createWeb3(await this.createWeb3Provider(options))
}

// Create an external provider from the basic request method.
Expand Down
5 changes: 4 additions & 1 deletion packages/plugins/EVM/src/state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { TransactionFormatter } from './TransactionFormatter'
import { TransactionWatcher } from './TransactionWatcher'
import type { EVM_Web3State } from './Connection/types'
import { IdentityService } from './IdentityService'
import { BalanceNotifier } from './BalanceNotifier'
import { BlockNumberNotifier } from './BlockNumberNotifier'

export function createWeb3State(context: Plugin.Shared.SharedContext): EVM_Web3State {
const Provider_ = new Provider(context)
Expand All @@ -22,7 +24,8 @@ export function createWeb3State(context: Plugin.Shared.SharedContext): EVM_Web3S
return {
Settings: Settings_,
Provider: Provider_,

BalanceNotifier: new BalanceNotifier(),
BlockNumberNotifier: new BlockNumberNotifier(),
AddressBook: new AddressBook(context, {
chainId: Provider_.chainId,
}),
Expand Down
Loading