diff --git a/frontend/src/pages/AccountReceive.vue b/frontend/src/pages/AccountReceive.vue index 63442477..d7472331 100644 --- a/frontend/src/pages/AccountReceive.vue +++ b/frontend/src/pages/AccountReceive.vue @@ -153,8 +153,16 @@ function useScan() { const mostRecentBlockNumber = ref(0); // Start and end blocks for advanced mode settings - const { advancedMode, startBlock, endBlock, setScanBlocks, setScanPrivateKey, scanPrivateKey, resetScanSettings } = - useSettingsStore(); + const { + advancedMode, + startBlock, + endBlock, + setScanBlocks, + setScanPrivateKey, + scanPrivateKey, + resetScanSettings, + getRegisteredBlockNumber, + } = useSettingsStore(); const { signer, userAddress: userWalletAddress, isAccountSetup, provider } = useWalletStore(); const startBlockLocal = ref(); const endBlockLocal = ref(); @@ -320,6 +328,7 @@ function useScan() { mostRecentBlockTimestamp.value = latestBlock.timestamp; // Default scan behavior for await (const announcementsBatch of umbra.value.fetchSomeAnnouncements( + getRegisteredBlockNumber(), signer.value, userWalletAddress.value, overrides diff --git a/frontend/src/store/settings.ts b/frontend/src/store/settings.ts index 308bf265..8f8f551d 100644 --- a/frontend/src/store/settings.ts +++ b/frontend/src/store/settings.ts @@ -12,6 +12,7 @@ const settings = { language: 'language', sendHistorySave: 'send-history-save', UmbraApiVersion: 'umbra-api-version', + registeredBlockNumber: 'registered-block-number', }; // Shared state between instances @@ -27,6 +28,7 @@ const startBlock = ref(undefined); // block number to start const endBlock = ref(undefined); // block number to scan through const scanPrivateKey = ref(); // private key entered when scanning const lastWallet = ref(); // name of last wallet used +const registeredBlockNumber = ref(undefined); // block number of the when the user registered const params = new URLSearchParams(window.location.search); const paramLocale = params.get('locale') || undefined; @@ -41,6 +43,9 @@ export default function useSettingsStore() { lastWallet.value = LocalStorage.getItem(settings.lastWallet) ? String(LocalStorage.getItem(settings.lastWallet)) : undefined; + registeredBlockNumber.value = LocalStorage.getItem(settings.registeredBlockNumber) + ? Number(LocalStorage.getItem(settings.registeredBlockNumber)) + : undefined; }); setLanguage( paramLocale @@ -137,6 +142,15 @@ export default function useSettingsStore() { LocalStorage.remove(settings.UmbraApiVersion); } + function getRegisteredBlockNumber() { + return registeredBlockNumber.value; + } + + function setRegisteredBlockNumber(blockNumber: number) { + registeredBlockNumber.value = blockNumber; + LocalStorage.set(settings.registeredBlockNumber, blockNumber); + } + return { toggleDarkMode, toggleAdvancedMode, @@ -158,5 +172,7 @@ export default function useSettingsStore() { getUmbraApiVersion, setUmbraApiVersion, clearUmbraApiVersion, + getRegisteredBlockNumber, + setRegisteredBlockNumber, }; } diff --git a/frontend/src/store/wallet.ts b/frontend/src/store/wallet.ts index 484daad1..e7afd5ff 100644 --- a/frontend/src/store/wallet.ts +++ b/frontend/src/store/wallet.ts @@ -631,9 +631,11 @@ const hasSetPublicKeysLegacy = async (name: string, provider: Provider) => { // Helper method to check if user has registered public keys in the StealthKeyRegistry async function getRegisteredStealthKeys(account: string, provider: Provider) { + const { setRegisteredBlockNumber } = useSettingsStore(); try { - const stealthPubKeys = await utils.lookupRecipient(account, provider); // throws if no keys found - return stealthPubKeys; + const registrationInfo = await utils.lookupRecipient(account, provider); // throws if no keys found + setRegisteredBlockNumber(Number(registrationInfo.block)); + return registrationInfo; } catch (err) { window.logger.warn(err); return null; diff --git a/umbra-js/src/classes/Umbra.ts b/umbra-js/src/classes/Umbra.ts index 1deba5e6..31ef4e44 100644 --- a/umbra-js/src/classes/Umbra.ts +++ b/umbra-js/src/classes/Umbra.ts @@ -30,9 +30,9 @@ import { invalidStealthAddresses, getEthSweepGasInfo, lookupRecipient, + getBlockNumberUserRegistered, assertSupportedAddress, checkSupportedAddresses, - getBlockNumberUserRegistered, recursiveGraphFetch, } from '../utils/utils'; import { Umbra as UmbraContract, Umbra__factory, ERC20__factory } from '../typechain'; @@ -419,16 +419,22 @@ export class Umbra { /** * @notice Fetches Umbra event logs starting from the block user registered their stealth keys in using * a subgraph, if available, falling back to RPC if not + * @param possibleRegisteredBlockNumber Block number when user registered their stealth keys (if known) + * @param Signer Signer with provider to use for fetching the block number (if not known) from the StealthKeyRegistry contract + * @param address Address of the user for fetching the block number (if not known) from the subgraph or StealthKeyRegistry contract * @param overrides Override the start and end block used for scanning; * @returns A list of Announcement events supplemented with additional metadata, such as the sender, block, * timestamp, and txhash + * @dev If the registered block number is not known, it will be fetched from the subgraph or the StealthKeyRegistry contract */ async *fetchSomeAnnouncements( + possibleRegisteredBlockNumber: number | undefined, Signer: JsonRpcSigner, address: string, overrides: ScanOverrides = {} ): AsyncGenerator { - const registeredBlockNumber = await getBlockNumberUserRegistered(address, Signer.provider, this.chainConfig); + const registeredBlockNumber = + possibleRegisteredBlockNumber || (await getBlockNumberUserRegistered(address, Signer.provider, this.chainConfig)); // Get start and end blocks to scan events for const startBlock = overrides.startBlock || registeredBlockNumber || this.chainConfig.startBlock; const endBlock = overrides.endBlock || 'latest'; diff --git a/umbra-js/src/utils/utils.ts b/umbra-js/src/utils/utils.ts index 42dd6d45..da95eb38 100644 --- a/umbra-js/src/utils/utils.ts +++ b/umbra-js/src/utils/utils.ts @@ -207,8 +207,8 @@ export async function toAddress(name: string, provider: EthersProvider) { /** * @notice Returns public keys from the recipientId * @dev When providing a public key, transaction hash, or address with advanced mode, the spending and viewing - * public keys will be the same. Only keys retrieved from the StealthKeyRegistry will have different spending - * and viewing keys + * public keys will be the same. Only keys retrieved from the StealthKeyRegistry (or the subgraph) will have different spending + * and viewing keys. Additionally, the block number when the user registered will be returned. * @param id Recipient identifier, must be an ENS name, CNS name, address, transaction hash, or public key * @param provider ethers provider to use * @param options Object containing lookup options: @@ -230,7 +230,7 @@ export async function lookupRecipient( const isPublicKey = id.length === 132 && isHexString(id); if (supportPubKey && isPublicKey) { assertValidPoint(id); - return { spendingPublicKey: id, viewingPublicKey: id }; + return { spendingPublicKey: id, viewingPublicKey: id, block: undefined }; } // Check if identifier is a transaction hash. If so, we recover the sender's public keys from the transaction @@ -238,7 +238,7 @@ export async function lookupRecipient( if (supportTxHash && isTxHash) { const publicKey = await recoverPublicKeyFromTransaction(id, provider); assertValidPoint(publicKey); - return { spendingPublicKey: publicKey, viewingPublicKey: publicKey }; + return { spendingPublicKey: publicKey, viewingPublicKey: publicKey, block: undefined }; } // The remaining checks are dependent on the advanced mode option. The provided identifier is now either an @@ -259,7 +259,11 @@ export async function lookupRecipient( stealthKeyChangedEvent.viewingPubKey, stealthKeyChangedEvent.viewingPubKeyPrefix.toString() ); - return { spendingPublicKey: spendingPublicKey, viewingPublicKey: viewingPublicKey }; + return { + spendingPublicKey: spendingPublicKey, + viewingPublicKey: viewingPublicKey, + block: stealthKeyChangedEvent.block, + }; } catch (error) { if (error instanceof Error) { console.log('Public key subgraph fetch error: ', error.message); @@ -268,7 +272,8 @@ export async function lookupRecipient( } console.log('Error using subgraph to lookup receipient stealth keys, will query registry contract'); const registry = new StealthKeyRegistry(provider); - return registry.getStealthKeys(address); + const { spendingPublicKey, viewingPublicKey } = await registry.getStealthKeys(address); + return { spendingPublicKey, viewingPublicKey, block: undefined }; } } @@ -277,7 +282,7 @@ export async function lookupRecipient( if (!txHash) throw new Error('Could not get public key because the provided account has not sent any transactions'); const publicKey = await recoverPublicKeyFromTransaction(txHash, provider); assertValidPoint(publicKey); - return { spendingPublicKey: publicKey, viewingPublicKey: publicKey }; + return { spendingPublicKey: publicKey, viewingPublicKey: publicKey, block: undefined }; } export async function getBlockNumberUserRegistered(