diff --git a/packages/site/src/components/Account.tsx b/packages/site/src/components/Account.tsx index d700310..cb89e03 100644 --- a/packages/site/src/components/Account.tsx +++ b/packages/site/src/components/Account.tsx @@ -1,9 +1,9 @@ import { MetaMaskContext, MetamaskActions, useAcount } from '../hooks'; import styled from 'styled-components'; -import { connectSnap, convertToEth, filterPendingRequests, getMMProvider, getSnap, handleCopyToClipboard, storeTxHash, trimAccount } from '../utils'; +import { connectSnap, convertToEth, filterPendingRequests, getSnap, handleCopyToClipboard, trimAccount } from '../utils'; import { FaCloudDownloadAlt, FaRegLightbulb } from 'react-icons/fa'; import { InstallFlaskButton, ConnectSnapButton, SimpleButton } from './Buttons'; -import { AccountActivity, AccountActivityType, SupportedChainIdMap, UserOperation } from '../types'; +import { AccountActivity, SupportedChainIdMap, UserOperation } from '../types'; import { useContext, useState } from 'react'; import { KeyringAccount, KeyringRequest } from "@metamask/keyring-api"; import { BlockieAccountModal } from './Blockie-Icon'; @@ -226,7 +226,8 @@ export const AccountModalDropdown = ({ closeModal(); await selectKeyringSnapAccount(account); await getSmartAccount(account.id); - await getAccountActivity(account.id); + // TODO: Add account activity + // await getAccountActivity(account.id); } const handleCreateAccount = async (event: any) => { @@ -670,128 +671,99 @@ export const AccountActivityDisplay = () => { const [state] = useContext(MetaMaskContext); const renderAccountActivityItem = (item: AccountActivity) => { - switch (item.type) { - case AccountActivityType.SmartContract: - if (item.userOperationReceipt === null) { - return ( - <> - -

Status:

-

Pending

-
- - -

UserOp hash:

- -

{trimAccount(item.userOpHash)}

- handleCopyToClipboard(e, item.userOpHash)}> - - -
-
- - ) - } else { - const sender = item.userOperationReceipt.sender; - const receipt = item.userOperationReceipt.receipt; + if (item.userOperationReceipt === null) { + return ( + <> + +

Status:

+

Pending

+
+ + +

UserOp hash:

+ +

{trimAccount(item.userOpHash)}

+ handleCopyToClipboard(e, item.userOpHash)}> + + +
+
+ + ) + } else { + const sender = item.userOperationReceipt.sender; + const receipt = item.userOperationReceipt.receipt; - return ( - <> - -

Status:

-

{item.userOperationReceipt.success? Confirmed: Failed}

-
- - {!item.userOperationReceipt.success && item.userOperationReceipt.reason && ( - -

Revert:

-

{item.userOperationReceipt.reason}

-
- )} - - -

Sender:

- -

eth:{trimAccount(item.userOperationReceipt.sender)}

- handleCopyToClipboard(e, sender)}> - - -
-
- - -

To:

- -

eth:{trimAccount(item.userOperationReceipt.receipt.to)}

- handleCopyToClipboard(e, receipt.to)}> - - -
-
- - -

Nonce:

-

{BigNumber.from(item.userOperationReceipt.nonce).toNumber()}

-
- - -

Actual Gas Used(units):

-

{BigNumber.from(item.userOperationReceipt.actualGasUsed).toNumber()}

-
- - -

Actual Gas Cost:

-

{convertToEth(BigNumber.from(item.userOperationReceipt.actualGasCost).toString())} ETH

-
- - -

UserOp hash:

- -

{trimAccount(item.userOperationReceipt.userOpHash)}

- handleCopyToClipboard(e, item.userOpHash)}> - - -
-
- - -

Transaction hash:

- -

{trimAccount(receipt.transactionHash)}

- handleCopyToClipboard(e, receipt.transactionHash)}> - - -
-
- - ) - } + return ( + <> + +

Status:

+

{item.userOperationReceipt.success? Confirmed: Failed}

+
- case AccountActivityType.EOA: - const txHash = item.txHash ? item.txHash : ''; - return ( - <> + {!item.userOperationReceipt.success && item.userOperationReceipt.reason && ( -

Status:

-

{Confirmed}

+

Revert:

+

{item.userOperationReceipt.reason}

- - -

Transaction hash:

- -

{trimAccount(txHash)}

- handleCopyToClipboard(e, txHash)}> - - -
-
- - ) - default: - return ( - <> - - ); + )} + + +

Sender:

+ +

eth:{trimAccount(item.userOperationReceipt.sender)}

+ handleCopyToClipboard(e, sender)}> + + +
+
+ + +

To:

+ +

eth:{trimAccount(item.userOperationReceipt.receipt.to)}

+ handleCopyToClipboard(e, receipt.to)}> + + +
+
+ + +

Nonce:

+

{BigNumber.from(item.userOperationReceipt.nonce).toNumber()}

+
+ + +

Actual Gas Used(units):

+

{BigNumber.from(item.userOperationReceipt.actualGasUsed).toNumber()}

+
+ + +

Actual Gas Cost:

+

{convertToEth(BigNumber.from(item.userOperationReceipt.actualGasCost).toString())} ETH

+
+ + +

UserOp hash:

+ +

{trimAccount(item.userOperationReceipt.userOpHash)}

+ handleCopyToClipboard(e, item.userOpHash)}> + + +
+
+ + +

Transaction hash:

+ +

{trimAccount(receipt.transactionHash)}

+ handleCopyToClipboard(e, receipt.transactionHash)}> + + +
+
+ + ) } } @@ -802,7 +774,7 @@ export const AccountActivityDisplay = () => { Type: - {item.type === AccountActivityType.SmartContract ? 'Withdraw': 'Deposit'} + Withdraw {renderAccountActivityItem(item)} diff --git a/packages/site/src/components/Card.tsx b/packages/site/src/components/Card.tsx index b926916..5fdca68 100644 --- a/packages/site/src/components/Card.tsx +++ b/packages/site/src/components/Card.tsx @@ -111,7 +111,7 @@ const BlockExplorerLink = styled.a` margin-top: auto; margin-bottom: 0; margin-right: 1rem; - color: black; + color: ${(props) => props.theme.colors.primary.default}; &:hover { color: ${(props) => props.theme.colors.text.default}; diff --git a/packages/site/src/components/Transaction.tsx b/packages/site/src/components/Transaction.tsx index dfd811d..7c46654 100644 --- a/packages/site/src/components/Transaction.tsx +++ b/packages/site/src/components/Transaction.tsx @@ -8,7 +8,7 @@ import { AccountRequestDisplay } from './Account'; import { convertToEth, convertToWei, estimateGas, trimAccount } from '../utils/eth'; import { BlockieAccountModal } from './Blockie-Icon'; import { BigNumber, ethers } from 'ethers'; -import { calcPreVerificationGas, estimatCreationGas, estimateUserOperationGas, getDummySignature, getMMProvider, getUserOpCallData, handleCopyToClipboard, notify, storeTxHash } from '../utils'; +import { calcPreVerificationGas, estimatCreationGas, estimateUserOperationGas, getDummySignature, getMMProvider, getUserOpCallData, handleCopyToClipboard, notify } from '../utils'; import { EntryPoint__factory } from '@account-abstraction/contracts'; import { UserOperation } from '../types'; import { FaCopy } from "react-icons/fa"; @@ -107,6 +107,7 @@ enum Stage { Loading = 'Loading', Success = 'Success', Failed = 'Failed', + UserConfirmation = 'User Confirmation', } export enum TransactionType { @@ -129,8 +130,6 @@ export const EthereumTransactionModalComponent = ({ const { sendRequestSync, getSmartAccount, - getAccountActivity, - getKeyringSnapAccounts, } = useAcount(); const handleDepositSubmit = async () => { @@ -154,13 +153,12 @@ export const EthereumTransactionModalComponent = ({ ); // check the selected account is connected - console.log(state) if (!state.isSelectedSnapKeyringAccountConnected) { throw new Error('The selected account is not connected. Please connect the account using Settings page.') } // send transaction - const res = await getMMProvider().request({ + const txHash = await getMMProvider().request({ method: 'eth_sendTransaction', params: [ { @@ -176,16 +174,11 @@ export const EthereumTransactionModalComponent = ({ }) as string // show success message - await getSmartAccount(state.selectedSnapKeyringAccount.id); - await storeTxHash( - state.selectedSnapKeyringAccount.id, - res, - state.chainId, - ); setAmount(''); setSuccessMessage(`${amount} ETH successfully depoisted to entry point contract.`); setStatus(Stage.Success); - // notify('Deposit Transaction sent (txHash)', 'View activity for details.', res) + notify('Transaction sent (txHash)', 'Check wallet activity for details.', txHash) + await getSmartAccount(state.selectedSnapKeyringAccount.id); } const handleWithdrawSubmit = async () => { @@ -258,11 +251,11 @@ export const EthereumTransactionModalComponent = ({ try { e.preventDefault(); - setStatus(Stage.Loading); - if (transactionType === TransactionType.Deposit) { + setStatus(Stage.UserConfirmation); await handleDepositSubmit(); } else if (transactionType === TransactionType.Withdraw) { + setStatus(Stage.Loading); await handleWithdrawSubmit(); } else { throw new Error('Invalid transaction type'); @@ -391,6 +384,12 @@ export const EthereumTransactionModalComponent = ({ ); + case Stage.UserConfirmation: + return ( + + + + ); case Stage.Failed: return ( diff --git a/packages/site/src/hooks/Accounts.tsx b/packages/site/src/hooks/Accounts.tsx index 52bf58d..5569592 100644 --- a/packages/site/src/hooks/Accounts.tsx +++ b/packages/site/src/hooks/Accounts.tsx @@ -1,7 +1,7 @@ import { useContext } from 'react'; import { MetamaskActions, MetaMaskContext } from '.'; -import { AccountActivity, AccountActivityType, BundlerUrls, SmartContractAccount } from "../types"; -import { bundlerUrls, fetchUserOpHashes, connectedAccounts, getChainId, getKeyringSnapRpcClient, getMMProvider, getNextRequestId, getScAccount, getTxHashes, getUserOperationReceipt, parseChainId, sendSupportedEntryPoints, listConnectedAccounts } from "../utils"; +import { AccountActivity, BundlerUrls, SmartContractAccount } from "../types"; +import { bundlerUrls, fetchUserOpHashes, connectedAccounts, getChainId, getKeyringSnapRpcClient, getMMProvider, getNextRequestId, getScAccount, getUserOperationReceipt, sendSupportedEntryPoints, listConnectedAccounts } from "../utils"; import { KeyringAccount } from "@metamask/keyring-api"; import { KeyringSnapRpcClient } from '@metamask/keyring-api'; import type { Json } from '@metamask/utils'; @@ -44,38 +44,6 @@ export const useAcount = () => { await getKeyringSnapAccounts() }; - const sendRequestAsync = async ( - keyringAccountId: string, - method: string, - params: any[] = [], - ): Promise<{ - pending: boolean; - redirect?: { - url: string; - message: string; - }; - }> => { - const id = await getNextRequestId(); - const result = await snapRpcClient.submitRequest({ - id: id.toString(), - account: keyringAccountId, - scope: 'async', - request: { - method, - params: params, - }, - }); - await getKeyringSnapAccounts(); - - return result as { - pending: boolean; - redirect?: { - url: string; - message: string; - }; - }; - }; - const sendRequestSync = async ( keyringAccountId: string, method: string, @@ -88,7 +56,7 @@ export const useAcount = () => { const result = await snapRpcClient.submitRequest({ id: id.toString(), account: keyringAccountId, - scope: 'sync', + scope: '', request: { method, params: params, @@ -144,24 +112,12 @@ export const useAcount = () => { for (const userOpHash of userOpHashes) { accountActivity.push( { - type: AccountActivityType.SmartContract, userOpHash, userOperationReceipt: await getUserOperationReceipt(userOpHash), } ) } - const txHashes = await getTxHashes(keyringAccountId, state.chainId); - for (const txHash of txHashes) { - accountActivity.push( - { - type: AccountActivityType.EOA, - txHash, - userOpHash: '', - userOperationReceipt: null, - } - ) - } dispatch({ type: MetamaskActions.SetAccountActivity, payload: accountActivity, @@ -244,7 +200,6 @@ export const useAcount = () => { getBundlerUrls, updateChainId, getWalletChainId, - sendRequestAsync, sendRequestSync, approveRequest, rejectRequest, diff --git a/packages/site/src/pages/index.tsx b/packages/site/src/pages/index.tsx index 03da03b..ef8c25f 100644 --- a/packages/site/src/pages/index.tsx +++ b/packages/site/src/pages/index.tsx @@ -137,7 +137,6 @@ const Index = () => { deleteAccount, selectKeyringSnapAccount, getSmartAccount, - getAccountActivity, getBundlerUrls, updateChainId, setWalletListener, diff --git a/packages/site/src/types/erc-4337.ts b/packages/site/src/types/erc-4337.ts index efa3815..9ee44b0 100644 --- a/packages/site/src/types/erc-4337.ts +++ b/packages/site/src/types/erc-4337.ts @@ -30,16 +30,9 @@ export type SmartContractAccount = { isAccountDeployed: boolean; }; -export enum AccountActivityType { - SmartContract = 'SmartContract', - EOA = 'EOA', -} - export type AccountActivity = { - type: AccountActivityType; userOpHash: string; userOperationReceipt: UserOperationReceipt | null; - txHash?: string; }; export type UserOperationReceipt = { diff --git a/packages/site/src/utils/snap.ts b/packages/site/src/utils/snap.ts index 2f224d0..0c9edb8 100644 --- a/packages/site/src/utils/snap.ts +++ b/packages/site/src/utils/snap.ts @@ -133,50 +133,6 @@ export const getScAccount = async ( } as SmartContractAccount; }; -export const storeTxHash = async ( - keyringAccountId: string, - txHash: string, - chainId: string, -): Promise => { - return (await getMMProvider().request({ - method: 'wallet_invokeSnap', - params: { - snapId: defaultSnapOrigin, - request: { - method: 'store_tx_hash', - params: [ - { - keyringAccountId, - txHash, - chainId, - }, - ], - }, - }, - })) as boolean; -}; - -export const getTxHashes = async ( - keyringAccountId: string, - chainId: string, -): Promise => { - return (await getMMProvider().request({ - method: 'wallet_invokeSnap', - params: { - snapId: defaultSnapOrigin, - request: { - method: 'get_tx_hashes', - params: [ - { - keyringAccountId, - chainId, - }, - ], - }, - }, - })) as string[]; -}; - export const fetchUserOpHashes = async ( keyringAccountId: string, ): Promise => { diff --git a/packages/snap/README.md b/packages/snap/README.md index 16b62ed..a439725 100644 --- a/packages/snap/README.md +++ b/packages/snap/README.md @@ -92,29 +92,6 @@ const result: SmartContractAccount = await window.ethereum.request({ console.log(result); ``` -#### get_user_ops_hashes - -Returns a list of confirmed operations hashes for the given keyring Account Id. - -```TS -const result: string[] = await window.ethereum.request({ - method: 'wallet_invokeSnap', - params: { - snapId: 'npm:@transeptor-labs/smarthub-snap', - request: { - method: 'get_user_ops_hashes', - params: [ - { - keyringAccountId: 'keyringAccountId', - }, - ], - }, - }, -}); - -console.log(result); -``` - #### clear_activity_data Clears all the smart account activity data from the snap storage. diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index dc1df73..8f19319 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/transeptorlabs/smarthub-snap.git" }, "source": { - "shasum": "LJX6bPvYD2+dDaWi5FJPS8gWOZaBNlKDs1vT7XSuBYI=", + "shasum": "Yr9XIGOf85XPrIwVcpUMubm5tKt+ztgxO52U55YaRsk=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/index.ts b/packages/snap/src/index.ts index 4e60dee..f61783e 100644 --- a/packages/snap/src/index.ts +++ b/packages/snap/src/index.ts @@ -16,19 +16,15 @@ import { storeBundlerUrl, getKeyRing, getUserOpHashes, - getTxHashes, - storeTxHash, getNextRequestId, } from './state'; import { EstimateCreationGasParams, EstimateUserOperationGas, - GetTxHashesParams, GetUserOpParams, NotifyParams, SmartAccountActivityParams, SmartAccountParams, - StoreTxHashParams, UserOpCallDataParams, } from './types'; import { @@ -223,25 +219,6 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ return JSON.stringify(result); } - case InternalMethod.GetTxHashes: { - const params: GetTxHashesParams = ( - request.params as any[] - )[0] as GetTxHashesParams; - return await getTxHashes(params.keyringAccountId, params.chainId); - } - - case InternalMethod.StoreTxHash: { - const params: StoreTxHashParams = ( - request.params as any[] - )[0] as StoreTxHashParams; - result = await storeTxHash( - params.keyringAccountId, - params.txHash, - params.chainId, - ); - return result; - } - case InternalMethod.AddBundlerUrl: { result = await storeBundlerUrl( (request.params as any[])[0], diff --git a/packages/snap/src/keyring/permissions.ts b/packages/snap/src/keyring/permissions.ts index 0e0229a..b5872a0 100644 --- a/packages/snap/src/keyring/permissions.ts +++ b/packages/snap/src/keyring/permissions.ts @@ -5,8 +5,6 @@ export enum InternalMethod { GetNextRequestId = 'get_next_request_id', SmartAccount = 'sc_account', GetUserOpsHashes = 'get_user_ops_hashes', - GetTxHashes = 'get_tx_hashes', - StoreTxHash = 'store_tx_hash', GetUserOpCallData = 'get_user_op_call_data', EstimateCreationGas = 'estimate_creation_gas', Notify = 'notify', @@ -70,8 +68,6 @@ export const PERMISSIONS = new Map([ InternalMethod.GetNextRequestId, InternalMethod.SmartAccount, InternalMethod.GetUserOpsHashes, - InternalMethod.GetTxHashes, - InternalMethod.StoreTxHash, InternalMethod.GetUserOpCallData, InternalMethod.EstimateCreationGas, InternalMethod.Notify, @@ -121,8 +117,6 @@ export const PERMISSIONS = new Map([ InternalMethod.GetNextRequestId, InternalMethod.SmartAccount, InternalMethod.GetUserOpsHashes, - InternalMethod.GetTxHashes, - InternalMethod.StoreTxHash, InternalMethod.GetUserOpCallData, InternalMethod.EstimateCreationGas, InternalMethod.Notify, diff --git a/packages/snap/src/state/state.ts b/packages/snap/src/state/state.ts index a45bebd..0db94df 100644 --- a/packages/snap/src/state/state.ts +++ b/packages/snap/src/state/state.ts @@ -13,7 +13,6 @@ export const getState = async ( scAccount: { [chainId: string]: { userOpHashes: string[]; - txHashes: string[]; }; }; }; @@ -31,7 +30,6 @@ export const getState = async ( scAccount: { [chainId: string]: { userOpHashes: string[]; - txHashes: string[]; }; }; }; @@ -53,23 +51,21 @@ export const getState = async ( scAccount: { '0x539': { userOpHashes: [], - txHashes: [], }, '0x1': { userOpHashes: [], - txHashes: [], }, '0x5': { userOpHashes: [], - txHashes: [], + }, + '0xaa36a7': { + userOpHashes: [], }, '0x89': { userOpHashes: [], - txHashes: [], }, '0x13881': { userOpHashes: [], - txHashes: [], }, }, }; @@ -133,32 +129,6 @@ export const storeBundlerUrl = async ( return true; }; -export const getTxHashes = async ( - keyringAccountId: string, - chainId: string, -): Promise => { - const state = await getState(keyringAccountId); - return state.smartAccountActivity[keyringAccountId].scAccount[chainId] - .txHashes; -}; - -export const storeTxHash = async ( - keyringAccountId: string, - txHash: string, - chainId: string, -): Promise => { - const state = await getState(keyringAccountId); - state.smartAccountActivity[keyringAccountId].scAccount[chainId].txHashes.push( - txHash, - ); - - await snap.request({ - method: 'snap_manageState', - params: { operation: 'update', newState: state }, - }); - return true; -}; - export const storeKeyRing = async (keyring: KeyringState): Promise => { const state = await getState(); state.keyringState = keyring; diff --git a/packages/snap/src/types/request.types.ts b/packages/snap/src/types/request.types.ts index 12e57d5..e6256ba 100644 --- a/packages/snap/src/types/request.types.ts +++ b/packages/snap/src/types/request.types.ts @@ -13,17 +13,6 @@ export type SmartAccountParams = { chainId: string; }; -export type GetTxHashesParams = { - keyringAccountId: string; - chainId: string; -}; - -export type StoreTxHashParams = { - keyringAccountId: string; - txHash: string; - chainId: string; -}; - export type UserOpCallDataParams = { keyringAccountId: string; to: string; diff --git a/packages/snap/test/state.test.ts b/packages/snap/test/state.test.ts index 9f1464d..34e299d 100644 --- a/packages/snap/test/state.test.ts +++ b/packages/snap/test/state.test.ts @@ -7,11 +7,9 @@ import { getKeyRing, getNextRequestId, getState, - getTxHashes, getUserOpHashes, storeBundlerUrl, storeKeyRing, - storeTxHash, storeUserOpHash, } from '../src/state'; import { restoreGlobal, setupSnapMock } from './testUtils'; @@ -68,11 +66,9 @@ describe('state module - State', () => { scAccount: { '0x1': { userOpHashes: ['hash1', 'hash2'], - txHashes: [], }, '0x5': { userOpHashes: ['hash3', 'hash4'], - txHashes: [], }, }, }, @@ -102,11 +98,9 @@ describe('state module - State', () => { scAccount: { '0x1': { userOpHashes: ['hash1', 'hash2'], - txHashes: [], }, '0x5': { userOpHashes: ['hash3', 'hash4'], - txHashes: [], }, }, }, @@ -169,73 +163,6 @@ describe('state module - State', () => { }); }); - describe('getTxHashes', () => { - it('should return an array of an users confirmed txHashes for a specific chainId', async () => { - const keyringAccountId = uuid(); - const mockState = { - keyringState: { - wallets: {}, - pendingRequests: {}, - signedTx: {}, - }, - requestIdCounter: 0, - bundlerUrls: DEFAULT_BUNDLER_URLS, - smartAccountActivity: { - [keyringAccountId]: { - scAccount: { - '0x1': { - userOpHashes: [], - txHashes: ['hash1', 'hash2'], - }, - }, - }, - }, - }; - - (global as any).snap.request.mockReturnValue(Promise.resolve(mockState)); - - const result = await getTxHashes(keyringAccountId, '0x1'); - expect(result).toStrictEqual(['hash1', 'hash2']); - }); - }); - - describe('storeTxHash', () => { - it('should store a user confirmed txHash', async () => { - const keyringAccountId = uuid(); - const mockState = { - keyringState: { - wallets: {}, - pendingRequests: {}, - }, - requestIdCounter: 0, - bundlerUrls: DEFAULT_BUNDLER_URLS, - smartAccountActivity: { - [keyringAccountId]: { - scAccount: { - '0x1': { - userOpHashes: [], - txHashes: ['hash1', 'hash2'], - }, - '0x5': { - userOpHashes: [], - txHashes: ['hash3', 'hash4'], - }, - }, - }, - }, - }; - - (global as any).snap.request.mockReturnValue(Promise.resolve(mockState)); - - const stateBefore = await getTxHashes(keyringAccountId, '0x1'); - expect(stateBefore).toStrictEqual(['hash1', 'hash2']); - - await storeTxHash(keyringAccountId, 'hash5', '0x1'); - const result = await getTxHashes(keyringAccountId, '0x1'); - expect(result).toStrictEqual(['hash1', 'hash2', 'hash5']); - }); - }); - describe('clearActivityData', () => { it('should clear all smart account Activity data', async () => { const keyringAccountId = uuid(); @@ -252,11 +179,9 @@ describe('state module - State', () => { scAccount: { '0x1': { userOpHashes: ['hash1', 'hash2'], - txHashes: [], }, '0x5': { userOpHashes: ['hash3', 'hash4'], - txHashes: [], }, }, },