From 5d5ad43ce533ef035bcdbe6e02bc767309664572 Mon Sep 17 00:00:00 2001 From: Jack Works <5390719+Jack-Works@users.noreply.github.com> Date: Thu, 19 Feb 2026 07:12:29 +0000 Subject: [PATCH] refactor: migrate decodeEvents --- .../components/RedPacketRecord.tsx | 2 +- .../SiteAdaptor/hooks/useCreateCallback.tsx | 17 +++-- .../hooks/useCreateFTRedpacketCallback.ts | 12 +--- .../hooks/useCreateNftRedPacketReceipt.ts | 18 ++--- .../hooks/useCreateNftRedpacketCallback.ts | 18 +++-- .../hooks/useCreateRedPacketReceipt.ts | 53 +++++---------- .../SiteAdaptor/hooks/useRedPacketContract.ts | 26 +++---- .../SiteAdaptor/views/NftRedPacketConfirm.tsx | 4 +- .../src/Web3/EVM/apis/ContractReadonlyAPI.ts | 32 ++++----- .../descriptors/RedPacket.ts | 27 +++----- .../EVM/state/TransactionFormatter/utils.ts | 12 ++-- .../evm/src/helpers/abiArrayToMappedObject.ts | 3 +- .../evm/src/helpers/decodeEvents.ts | 68 ++++++++++--------- .../evm/tests/helpers/decodeEvents.ts | 27 ++------ 14 files changed, 141 insertions(+), 178 deletions(-) diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketRecord.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketRecord.tsx index 09de99c13714..821fbbc6ae4d 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketRecord.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/components/RedPacketRecord.tsx @@ -247,7 +247,7 @@ export const RedPacketRecord = memo(function RedPacketRecord({ message: createSuccessResult.message, }, total: history.total_amounts ?? '0', - duration: +createSuccessResult.duration, + duration: Number(createSuccessResult.duration), token: { type: TokenType.Fungible, schema: diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateCallback.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateCallback.tsx index 7bb93459060f..c1c9af70cdd5 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateCallback.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateCallback.tsx @@ -11,11 +11,13 @@ import { SchemaType, useTokenConstants, decodeEvents, + type MultipleAbiEventsToMappedObject, ContractTransaction, type GasConfig, + type TransactionReceipt, } from '@masknet/web3-shared-evm' import { EVMWeb3 } from '@masknet/web3-providers' -import { useRedPacketContract } from './useRedPacketContract.js' +import { getRedPacketContractAbi, useRedPacketContract } from './useRedPacketContract.js' export interface RedPacketSettings { shares: number @@ -151,7 +153,14 @@ export function useCreateCallback( const redPacketContract = useRedPacketContract(chainId, version) const getCreateParams = useCreateParamsCallback(expectedChainId, redPacketSettings, version, publicKey) - return useAsyncFn(async () => { + return useAsyncFn(async (): Promise< + | { + hash: string + receipt: TransactionReceipt + events: undefined | MultipleAbiEventsToMappedObject> + } + | undefined + > => { const token = redPacketSettings.token const createParams = await getCreateParams() if (!token || !redPacketContract || !createParams) return @@ -184,7 +193,7 @@ export function useCreateCallback( }) const receipt = await EVMWeb3.getTransactionReceipt(hash, { chainId }) if (receipt) { - const events = decodeEvents(redPacketContract.options.jsonInterface, receipt.logs) + const events = decodeEvents(getRedPacketContractAbi(version), receipt.logs) return { hash, @@ -192,6 +201,6 @@ export function useCreateCallback( events, } } - return { hash, receipt } + return { hash, receipt, events: undefined } }, [account, redPacketContract, redPacketSettings.token, gasOption, chainId, getCreateParams]) } diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateFTRedpacketCallback.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateFTRedpacketCallback.ts index a626f3370041..3bf8b55d77f1 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateFTRedpacketCallback.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateFTRedpacketCallback.ts @@ -68,16 +68,10 @@ export function useCreateFTRedpacketCallback( if (!settings.token) return - const CreationSuccess = (events?.CreationSuccess?.returnValues ?? {}) as { - creation_time: string - creator: string - id: string - token_address: string - total: string - } + const CreationSuccess = events?.CreationSuccess?.returnValues // the events log is not available - if (!events?.CreationSuccess?.returnValues.id) return + if (!CreationSuccess?.id) return const senderName = settings.name || getLastRecognizedIdentity()?.identifier?.userId const redpacketPayload = { sender: { @@ -90,7 +84,7 @@ export function useCreateFTRedpacketCallback( rpid: CreationSuccess.id, total: CreationSuccess.total, duration: settings.duration, - creation_time: Number.parseInt(CreationSuccess.creation_time, 10) * 1000, + creation_time: Number(CreationSuccess.creation_time * 1000n), token: settings.token, } as const Object.assign(payload.current, redpacketPayload) diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedPacketReceipt.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedPacketReceipt.ts index 9e54fe289f65..312b49f05c67 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedPacketReceipt.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedPacketReceipt.ts @@ -1,11 +1,10 @@ import { useAsyncRetry } from 'react-use' -import type { AbiItem } from 'web3-utils' import type { NetworkPluginID } from '@masknet/shared-base' import { type ChainId, useNftRedPacketConstants, decodeEvents } from '@masknet/web3-shared-evm' -import NFT_REDPACKET_ABI from '@masknet/web3-contracts/abis/NftRedPacket.json' with { type: 'json' } import { useChainContext } from '@masknet/web3-hooks-base' import { isSameAddress } from '@masknet/web3-shared-base' import { EVMWeb3 } from '@masknet/web3-providers' +import { NftRedPacketAbi } from '@masknet/web3-contracts/types/NftRedPacket.js' export function useCreateNftRedPacketReceipt(txid: string, expectedChainId: ChainId) { const { chainId } = useChainContext({ chainId: expectedChainId }) @@ -19,19 +18,12 @@ export function useCreateNftRedPacketReceipt(txid: string, expectedChainId: Chai const log = receipt.logs.find((log) => isSameAddress(log.address, RED_PACKET_NFT_ADDRESS)) if (!log) return null - const eventParams = decodeEvents(NFT_REDPACKET_ABI as AbiItem[], [log]) as unknown as { - CreationSuccess: { - returnValues: { - id: string - creation_time: string - } - } - } + const eventParams = decodeEvents(NftRedPacketAbi, [log]) - const { returnValues } = eventParams.CreationSuccess + const { returnValues } = eventParams.CreationSuccess! return { - rpid: returnValues.id || '', - creation_time: Number.parseInt(returnValues.creation_time, 10) * 1000, + rpid: returnValues.id, + creation_time: Number(returnValues.creation_time * 1000n), } }, [txid, chainId, RED_PACKET_NFT_ADDRESS]) } diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedpacketCallback.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedpacketCallback.ts index 042ff08a31e5..70e958ae0946 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedpacketCallback.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateNftRedpacketCallback.ts @@ -1,5 +1,5 @@ import type { NetworkPluginID } from '@masknet/shared-base' -import type { NftRedPacket } from '@masknet/web3-contracts/types/NftRedPacket.js' +import { NftRedPacketAbi, type NftRedPacket } from '@masknet/web3-contracts/types/NftRedPacket.js' import { useChainContext } from '@masknet/web3-hooks-base' import { useGasConfig } from '@masknet/web3-hooks-evm' import { EVMWeb3 } from '@masknet/web3-providers' @@ -9,6 +9,8 @@ import { decodeEvents, type GasConfig, isValidAddress, + type MultipleAbiEventsToMappedObject, + type TransactionReceipt, } from '@masknet/web3-shared-evm' import { useQuery } from '@tanstack/react-query' import { BigNumber } from 'bignumber.js' @@ -82,7 +84,15 @@ export function useCreateNftRedpacketCallback({ return new BigNumber(gasPrice).multipliedBy(gasLimit).multipliedBy(1.5).toFixed() }, [gasLimit, gasPrice]) - const [{ loading }, createCallback] = useAsyncFn(async () => { + const [{ loading }, createCallback] = useAsyncFn(async (): Promise< + | undefined + | { + hash: string + receipt: TransactionReceipt + events: undefined | MultipleAbiEventsToMappedObject + } + | undefined + > => { const nftRedPacketContract = createNftRedpacketContract(chainId) if (!nftRedPacketContract || !isValidAddress(contractAddress) || tokenIds.length === 0 || !gasLimit) { return @@ -115,10 +125,10 @@ export function useCreateNftRedpacketCallback({ return { hash, receipt, - events: decodeEvents(nftRedPacketContract.options.jsonInterface, receipt.logs), + events: decodeEvents(NftRedPacketAbi, receipt.logs), } } - return { hash, receipt } + return { hash, receipt, events: undefined } }, [duration, message, creator, contractAddress, tokenIds, account, chainId, gasOption, gasLimit]) return { gasLimit, estimateGasFee, loading, createCallback } diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts index 1de3d926e2eb..20b181a05207 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useCreateRedPacketReceipt.ts @@ -1,30 +1,16 @@ import { NetworkPluginID } from '@masknet/shared-base' -import REDPACKET_ABI from '@masknet/web3-contracts/abis/HappyRedPacketV4.json' with { type: 'json' } import { useEnvironmentContext, useWeb3Connection } from '@masknet/web3-hooks-base' import { isSameAddress } from '@masknet/web3-shared-base' -import { type ChainId, decodeEvents, useRedPacketConstants } from '@masknet/web3-shared-evm' +import { + type AbiEventToPrimitiveType, + type ChainId, + decodeEvents, + useRedPacketConstants, +} from '@masknet/web3-shared-evm' import { useQuery } from '@tanstack/react-query' -import type { AbiItem } from 'web3-utils' import { getRedpacket } from '../helpers/getRedpacket.js' +import { HappyRedPacketV4Abi } from '@masknet/web3-contracts/types/HappyRedPacketV4.js' -type CreationSuccessEventParams = { - id: string - /** seconds in string */ - creation_time: string - /** creator wallet address */ - creator: string - /** seconds in string */ - duration: '86400' - ifrandom: boolean - message: string - /** creator's name */ - name: string - /** account in string*/ - number: string - token_address: HexString - /** account in string*/ - total: string -} export function useCreateRedPacketReceipt(txHashOrAccountId: string, chainId: ChainId, enabled?: boolean) { const { pluginID } = useEnvironmentContext() const { HAPPY_RED_PACKET_ADDRESS_V4 } = useRedPacketConstants(chainId) @@ -34,7 +20,7 @@ export function useCreateRedPacketReceipt(txHashOrAccountId: string, chainId: Ch enabled, // eslint-disable-next-line @tanstack/query/exhaustive-deps queryKey: ['redpacket', 'creation-success-params', chainId, txHashOrAccountId], - queryFn: async () => { + queryFn: async (): Promise | null> => { if (!txHashOrAccountId || !Web3) return null if (pluginID === NetworkPluginID.PLUGIN_EVM) { @@ -44,27 +30,22 @@ export function useCreateRedPacketReceipt(txHashOrAccountId: string, chainId: Ch const log = receipt.logs.find((log) => isSameAddress(log.address, HAPPY_RED_PACKET_ADDRESS_V4)) if (!log) return null - const eventParams = decodeEvents(REDPACKET_ABI as AbiItem[], [log]) as unknown as { - CreationSuccess: { - returnValues: CreationSuccessEventParams - } - } - - return eventParams.CreationSuccess.returnValues + const eventParams = decodeEvents(HappyRedPacketV4Abi, [log]) + return eventParams.CreationSuccess!.returnValues } const result = await getRedpacket(txHashOrAccountId) return { - id: txHashOrAccountId, - creation_time: result.createTime.toString(), - creator: result.creator.toBase58(), - duration: '86400', + id: txHashOrAccountId as `0x${string}`, + creation_time: BigInt(result.createTime.toString()), + creator: result.creator.toBase58() as `0x${string}`, + duration: 86400n, ifrandom: result.ifSpiltRandom, message: result.message, name: result.name, - number: result.totalNumber.toString(), - token_address: result.tokenAddress.toBase58(), - total: result.totalAmount.toString(), + number: BigInt(result.totalNumber.toString()), + token_address: result.tokenAddress.toBase58() as `0x${string}`, + total: BigInt(result.totalAmount.toString()), } }, }) diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRedPacketContract.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRedPacketContract.ts index 4d927133597c..7b853708814f 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRedPacketContract.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useRedPacketContract.ts @@ -1,14 +1,9 @@ import { useContract } from '@masknet/web3-hooks-evm' -import HappyRedPacketV1ABI from '@masknet/web3-contracts/abis/HappyRedPacketV1.json' with { type: 'json' } -import HappyRedPacketV2ABI from '@masknet/web3-contracts/abis/HappyRedPacketV2.json' with { type: 'json' } -import HappyRedPacketV3ABI from '@masknet/web3-contracts/abis/HappyRedPacketV3.json' with { type: 'json' } -import HappyRedPacketV4ABI from '@masknet/web3-contracts/abis/HappyRedPacketV4.json' with { type: 'json' } -import type { HappyRedPacketV1 } from '@masknet/web3-contracts/types/HappyRedPacketV1.js' -import type { HappyRedPacketV2 } from '@masknet/web3-contracts/types/HappyRedPacketV2.js' -import type { HappyRedPacketV3 } from '@masknet/web3-contracts/types/HappyRedPacketV3.js' -import type { HappyRedPacketV4 } from '@masknet/web3-contracts/types/HappyRedPacketV4.js' +import { HappyRedPacketV1Abi, type HappyRedPacketV1 } from '@masknet/web3-contracts/types/HappyRedPacketV1.js' +import { HappyRedPacketV2Abi, type HappyRedPacketV2 } from '@masknet/web3-contracts/types/HappyRedPacketV2.js' +import { HappyRedPacketV3Abi, type HappyRedPacketV3 } from '@masknet/web3-contracts/types/HappyRedPacketV3.js' +import { HappyRedPacketV4Abi, type HappyRedPacketV4 } from '@masknet/web3-contracts/types/HappyRedPacketV4.js' import { type ChainId, useRedPacketConstants } from '@masknet/web3-shared-evm' -import type { AbiItem } from 'web3-utils' export function useRedPacketContract(chainId: ChainId, version: number) { const { @@ -17,10 +12,15 @@ export function useRedPacketContract(chainId: ChainId, version: number) { HAPPY_RED_PACKET_ADDRESS_V3: addressV3, HAPPY_RED_PACKET_ADDRESS_V4: addressV4, } = useRedPacketConstants(chainId) - const v1 = useContract(chainId, addressV1, HappyRedPacketV1ABI as AbiItem[]) - const v2 = useContract(chainId, addressV2, HappyRedPacketV2ABI as AbiItem[]) - const v3 = useContract(chainId, addressV3, HappyRedPacketV3ABI as AbiItem[]) - const v4 = useContract(chainId, addressV4, HappyRedPacketV4ABI as AbiItem[]) + const v1 = useContract(chainId, addressV1, HappyRedPacketV1Abi) + const v2 = useContract(chainId, addressV2, HappyRedPacketV2Abi) + const v3 = useContract(chainId, addressV3, HappyRedPacketV3Abi) + const v4 = useContract(chainId, addressV4, HappyRedPacketV4Abi) const versions = [v1, v2, v3, v4] as const return versions[version - 1] } + +export function getRedPacketContractAbi(version: number) { + const versions = [HappyRedPacketV1Abi, HappyRedPacketV2Abi, HappyRedPacketV3Abi, HappyRedPacketV4Abi] as const + return versions[version - 1]! +} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/views/NftRedPacketConfirm.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/views/NftRedPacketConfirm.tsx index c998f7ee3231..54de92beff0c 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/views/NftRedPacketConfirm.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/views/NftRedPacketConfirm.tsx @@ -150,9 +150,7 @@ export function NftRedPacketConfirm() { if (typeof receipt?.transactionHash !== 'string') return setTransactionId(receipt.transactionHash) RedPacketRPC.addRedPacketNft({ id: receipt.transactionHash, password: privateKey, contract_version: 1 }) - const { id } = (events?.CreationSuccess?.returnValues ?? {}) as { - id?: string - } + const { id } = events?.CreationSuccess?.returnValues ?? {} if (!id) return onSendPost(id) navigate(RoutePaths.Exit) diff --git a/packages/web3-providers/src/Web3/EVM/apis/ContractReadonlyAPI.ts b/packages/web3-providers/src/Web3/EVM/apis/ContractReadonlyAPI.ts index 63e37ce580b6..ca04b4458cb0 100644 --- a/packages/web3-providers/src/Web3/EVM/apis/ContractReadonlyAPI.ts +++ b/packages/web3-providers/src/Web3/EVM/apis/ContractReadonlyAPI.ts @@ -11,14 +11,14 @@ import type { Wallet } from '@masknet/web3-contracts/types/Wallet.js' import type { BaseContract } from '@masknet/web3-contracts/types/types.js' import type { AirdropV2 } from '@masknet/web3-contracts/types/AirdropV2.js' -import AirDropV2ABI from '@masknet/web3-contracts/abis/AirdropV2.json' with { type: 'json' } -import BalanceCheckerABI from '@masknet/web3-contracts/abis/BalanceChecker.json' with { type: 'json' } -import ERC20ABI from '@masknet/web3-contracts/abis/ERC20.json' with { type: 'json' } -import ERC20Bytes32ABI from '@masknet/web3-contracts/abis/ERC20Bytes32.json' with { type: 'json' } -import ERC165ABI from '@masknet/web3-contracts/abis/ERC165.json' with { type: 'json' } -import ERC721ABI from '@masknet/web3-contracts/abis/ERC721.json' with { type: 'json' } -import ERC1155ABI from '@masknet/web3-contracts/abis/ERC1155.json' with { type: 'json' } -import WalletABI from '@masknet/web3-contracts/abis/Wallet.json' with { type: 'json' } +import { AirdropV2Abi as AirDropV2ABI } from '@masknet/web3-contracts/types/AirdropV2.js' +import { BalanceCheckerAbi as BalanceCheckerABI } from '@masknet/web3-contracts/types/BalanceChecker.js' +import { ERC20Abi as ERC20ABI } from '@masknet/web3-contracts/types/ERC20.js' +import { ERC20Bytes32Abi as ERC20Bytes32ABI } from '@masknet/web3-contracts/types/ERC20Bytes32.js' +import { ERC165Abi as ERC165ABI } from '@masknet/web3-contracts/types/ERC165.js' +import { ERC721Abi as ERC721ABI } from '@masknet/web3-contracts/types/ERC721.js' +import { ERC1155Abi as ERC1155ABI } from '@masknet/web3-contracts/types/ERC1155.js' +import { WalletAbi as WalletABI } from '@masknet/web3-contracts/types/Wallet.js' import { EVMRequestReadonlyAPI } from './RequestReadonlyAPI.js' import type { EVMConnectionOptions } from '../types/index.js' @@ -46,35 +46,35 @@ export class EVMContractReadonlyAPI { } getERC20Contract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, ERC20ABI as AbiItem[], initial) + return this.getWeb3Contract(address, ERC20ABI, initial) } getERC20Bytes32Contract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, ERC20Bytes32ABI as AbiItem[], initial) + return this.getWeb3Contract(address, ERC20Bytes32ABI, initial) } getERC721Contract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, ERC721ABI as AbiItem[], initial) + return this.getWeb3Contract(address, ERC721ABI, initial) } getERC1155Contract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, ERC1155ABI as AbiItem[], initial) + return this.getWeb3Contract(address, ERC1155ABI, initial) } getERC165Contract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, ERC165ABI as AbiItem[], initial) + return this.getWeb3Contract(address, ERC165ABI, initial) } getBalanceCheckerContract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, BalanceCheckerABI as AbiItem[], initial) + return this.getWeb3Contract(address, BalanceCheckerABI, initial) } getWalletContract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, WalletABI as AbiItem[], initial) + return this.getWeb3Contract(address, WalletABI, initial) } getAirdropV2Contract(address: string | undefined, initial?: EVMConnectionOptions) { - return this.getWeb3Contract(address, AirDropV2ABI as AbiItem[], initial) + return this.getWeb3Contract(address, AirDropV2ABI, initial) } } export const EVMContractReadonly = EVMContractReadonlyAPI.Default diff --git a/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/descriptors/RedPacket.ts b/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/descriptors/RedPacket.ts index 1abbe4907e3a..54007d5e14fe 100644 --- a/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/descriptors/RedPacket.ts +++ b/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/descriptors/RedPacket.ts @@ -1,4 +1,3 @@ -import type { AbiItem } from 'web3-utils' import { type ChainId, getNftRedPacketConstant, @@ -6,22 +5,17 @@ import { type AbiFunctionToObjectMapped, type TransactionParameter, } from '@masknet/web3-shared-evm' -import HappyRedPacketV4ABI from '@masknet/web3-contracts/abis/HappyRedPacketV4.json' with { type: 'json' } -import NftRedPacketABI from '@masknet/web3-contracts/abis/NftRedPacket.json' with { type: 'json' } import { isSameAddress, type TransactionContext } from '@masknet/web3-shared-base' import type { TransactionDescriptorFormatResult } from '../types.js' import { DescriptorWithTransactionDecodedReceipt, getTokenAmountDescription } from '../utils.js' -import type { HappyRedPacketV4Abi } from '@masknet/web3-contracts/types/HappyRedPacketV4.js' -import type { NftRedPacketAbi } from '@masknet/web3-contracts/types/NftRedPacket.js' +import { HappyRedPacketV4Abi } from '@masknet/web3-contracts/types/HappyRedPacketV4.js' +import { NftRedPacketAbi } from '@masknet/web3-contracts/types/NftRedPacket.js' export class RedPacketDescriptor extends DescriptorWithTransactionDecodedReceipt { async getClaimTokenInfo(chainId: ChainId, contractAddress: string | undefined, hash: string | undefined) { - const events = await this.getReceipt(chainId, contractAddress, HappyRedPacketV4ABI as AbiItem[], hash) + const events = await this.getReceipt(chainId, contractAddress, HappyRedPacketV4Abi, hash) - const { claimed_value, token_address } = (events?.ClaimSuccess?.returnValues ?? {}) as { - claimed_value: string - token_address: string - } + const { claimed_value, token_address } = events?.ClaimSuccess?.returnValues ?? {} if (!token_address) return const token = await this.Hub.getFungibleToken(token_address ?? '', { chainId }) @@ -31,12 +25,9 @@ export class RedPacketDescriptor extends DescriptorWithTransactionDecodedReceipt } async getRefundTokenInfo(chainId: ChainId, contractAddress: string | undefined, hash: string | undefined) { - const events = await this.getReceipt(chainId, contractAddress, HappyRedPacketV4ABI as AbiItem[], hash) + const events = await this.getReceipt(chainId, contractAddress, HappyRedPacketV4Abi, hash) - const { remaining_balance, token_address } = (events?.RefundSuccess?.returnValues ?? {}) as { - token_address: string - remaining_balance: string - } + const { remaining_balance, token_address } = events?.RefundSuccess?.returnValues ?? {} if (!token_address) return @@ -47,11 +38,9 @@ export class RedPacketDescriptor extends DescriptorWithTransactionDecodedReceipt } async getClaimedNFTSymbol(chainId: ChainId, contractAddress: string | undefined, hash: string | undefined) { - const events = await this.getReceipt(chainId, contractAddress, NftRedPacketABI as AbiItem[], hash) + const events = await this.getReceipt(chainId, contractAddress, NftRedPacketAbi, hash) - const { token_address } = (events?.ClaimSuccess?.returnValues ?? {}) as { - token_address: string - } + const { token_address } = events?.ClaimSuccess?.returnValues ?? {} if (!token_address) return return this.getNonFungibleContractSymbol(chainId, token_address) diff --git a/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/utils.ts b/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/utils.ts index ebd24849d7e4..ba5b59c4d88d 100644 --- a/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/utils.ts +++ b/packages/web3-providers/src/Web3/EVM/state/TransactionFormatter/utils.ts @@ -1,8 +1,7 @@ -import type { AbiItem } from 'web3-utils' import { type FungibleToken, scale10, formatBalance } from '@masknet/web3-shared-base' import { type ChainId, type SchemaType, decodeEvents } from '@masknet/web3-shared-evm' -import { EVMContractReadonly } from '../../apis/ContractReadonlyAPI.js' import { BaseDescriptor } from './descriptors/Base.js' +import type { Abi } from 'viem' export function getTokenAmountDescription(amount: string | bigint = '0', token?: FungibleToken) { amount = String(amount) @@ -15,10 +14,10 @@ export function getTokenAmountDescription(amount: string | bigint = '0', token?: } export class DescriptorWithTransactionDecodedReceipt extends BaseDescriptor { - async getReceipt( + protected async getReceipt( chainId: ChainId, contractAddress: string | undefined, - abi: AbiItem[] | undefined, + abi: abi | undefined, hash: string | undefined, ) { if (!hash || !contractAddress || !abi) return @@ -26,9 +25,6 @@ export class DescriptorWithTransactionDecodedReceipt extends BaseDescriptor { const receipt = await this.Web3.getTransactionReceipt(hash, { chainId }) if (!receipt) return - const contract = EVMContractReadonly.getWeb3Contract(contractAddress, abi) - if (!contract) return - - return decodeEvents(contract.options.jsonInterface, receipt.logs) + return decodeEvents(abi, receipt.logs) } } diff --git a/packages/web3-shared/evm/src/helpers/abiArrayToMappedObject.ts b/packages/web3-shared/evm/src/helpers/abiArrayToMappedObject.ts index 8d2e8183f9f4..346c4d54e5ae 100644 --- a/packages/web3-shared/evm/src/helpers/abiArrayToMappedObject.ts +++ b/packages/web3-shared/evm/src/helpers/abiArrayToMappedObject.ts @@ -6,6 +6,7 @@ import type { ExtractAbiFunction, ExtractAbiFunctionNames, } from 'abitype' +import type { UnionEvaluate } from 'viem' /** * Convert [a: type1, b: type2] to { a: type1, b: type2 }. @@ -28,7 +29,7 @@ export function abiArrayToMappedObject export type AbiParametersToPrimitiveTypesObjectMapped< abiParameters extends readonly AbiParameter[], abiParameterKind extends AbiParameterKind = AbiParameterKind, -> = AbiParametersToPrimitiveTypesObjectMapped_inner +> = UnionEvaluate> export type AbiFunctionToObjectMapped< abi extends Abi, diff --git a/packages/web3-shared/evm/src/helpers/decodeEvents.ts b/packages/web3-shared/evm/src/helpers/decodeEvents.ts index 2fd937fc2b59..a69486153160 100644 --- a/packages/web3-shared/evm/src/helpers/decodeEvents.ts +++ b/packages/web3-shared/evm/src/helpers/decodeEvents.ts @@ -1,37 +1,43 @@ import type { EventLog, Log } from 'web3-core' -import defer * as web3_utils from 'web3-utils' -import type { AbiItem } from 'web3-utils' -import { abiCoder } from './abiCoder.js' +import { decodeEventLog, type ContractEventName, type DecodeEventLogReturnType } from 'viem' +import type { Abi, ExtractAbiEventNames } from 'abitype' -export function decodeEvents(abis: AbiItem[], logs: Log[]) { - // the topic0 for identifying which abi to be used for decoding the event - const listOfTopic0 = abis.map((abi) => - web3_utils.keccak256(`${abi.name}(${abi.inputs?.map((x) => x.type).join(',')})`), - ) +export type AbiEventToPrimitiveType< + abi extends Abi, + eventName extends ContractEventName, +> = DecodeEventLogReturnType['args'] - // decode events - const events = logs.map((log) => { - const idx = listOfTopic0.indexOf(log.topics[0]) - if (idx === -1) return - const abi = abis[idx] - const inputs = abi?.inputs ?? [] +export type MultipleAbiEventsToMappedObject = { + [eventName in ExtractAbiEventNames & ContractEventName]: + | (Omit & { returnValues: AbiEventToPrimitiveType }) + | undefined +} - return { - // more: https://web3js.readthedocs.io/en/v1.2.11/web3-eth-abi.html?highlight=decodeLog#decodelog - returnValues: abiCoder.decodeLog(inputs, log.data, abi.anonymous ? log.topics : log.topics.slice(1)), - raw: { - data: log.data, - topics: log.topics, - }, - event: abi.name, - signature: listOfTopic0[idx], - ...log, - } as EventLog - }) - return events.reduce<{ +export function decodeEvents(allAbis: abi, logs: Log[]): MultipleAbiEventsToMappedObject { + const events: { [eventName: string]: EventLog | undefined - }>((accumulate, event) => { - if (event) accumulate[event.event] = event - return accumulate - }, {}) + } = {} + // decode events + for (const log of logs) { + try { + const { eventName, args } = decodeEventLog({ + abi: allAbis, + topics: log.topics as [signature: `0x${string}`, ...args: Array<`0x${string}`>], + data: log.data as `0x${string}`, + }) + if (!eventName) continue + events[eventName] = { + returnValues: args, + raw: { + data: log.data, + topics: log.topics, + }, + event: eventName, + ...log, + } + } catch { + continue + } + } + return events as any } diff --git a/packages/web3-shared/evm/tests/helpers/decodeEvents.ts b/packages/web3-shared/evm/tests/helpers/decodeEvents.ts index e9740be2f74e..45dbfd92b613 100644 --- a/packages/web3-shared/evm/tests/helpers/decodeEvents.ts +++ b/packages/web3-shared/evm/tests/helpers/decodeEvents.ts @@ -1,8 +1,7 @@ import { describe, expect, it } from 'vitest' -import REDPACKET_ABI from '@masknet/web3-contracts/abis/HappyRedPacketV4.json' with { type: 'json' } import { decodeEvents } from '../../src/helpers/decodeEvents.js' -import type { AbiItem } from 'web3-utils' import { type Log } from 'web3-core' +import { HappyRedPacketV4Abi } from '@masknet/web3-contracts/types/HappyRedPacketV4.js' describe('decodeEvents', () => { it('should return expected result for RedPacket events log', () => { @@ -17,7 +16,7 @@ describe('decodeEvents', () => { transactionHash: '0x9a9e084717d1f63be9d552ffe4f1edbcad2ade10c064b72c1872b67856dfd278', transactionIndex: 0x58, } - const eventParams = decodeEvents(REDPACKET_ABI as AbiItem[], [log]) + const eventParams = decodeEvents(HappyRedPacketV4Abi, [log]) expect(eventParams).toMatchInlineSnapshot(` { "CreationSuccess": { @@ -34,30 +33,18 @@ describe('decodeEvents', () => { ], }, "removed": false, - "returnValues": Result { - "0": "100000000000000", - "1": "0x79db2b3db406f16477c48ac4dedc681995686bd3976bae159e9b3337429c4c6c", - "2": "Unknown User", - "3": "", - "4": "0x790116d0685eB197B886DAcAD9C247f785987A4a", - "5": "1686116399", - "6": "0xf4d2888d29D722226FafA5d9B24F9164c092421E", - "7": "2", - "8": true, - "9": "86400", - "__length__": 10, - "creation_time": "1686116399", + "returnValues": { + "creation_time": 1686116399n, "creator": "0x790116d0685eB197B886DAcAD9C247f785987A4a", - "duration": "86400", + "duration": 86400n, "id": "0x79db2b3db406f16477c48ac4dedc681995686bd3976bae159e9b3337429c4c6c", "ifrandom": true, "message": "", "name": "Unknown User", - "number": "2", + "number": 2n, "token_address": "0xf4d2888d29D722226FafA5d9B24F9164c092421E", - "total": "100000000000000", + "total": 100000000000000n, }, - "signature": "0x86af556fd7cfab9462285ad44f2d5913527c539ff549f74731ca9997ca534018", "topics": [ "0x86af556fd7cfab9462285ad44f2d5913527c539ff549f74731ca9997ca534018", ],