-
Notifications
You must be signed in to change notification settings - Fork 106
chore: track walletType usage #774
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
Changes from 1 commit
c8d3c13
d24935d
5b31cfc
392d655
7a740a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import LRUMap from "mnemonist/lru-map"; | ||
| import { getAddress } from "thirdweb"; | ||
| import { z } from "zod"; | ||
| import type { PrismaTransaction } from "../../schema/prisma"; | ||
|
|
@@ -8,7 +9,7 @@ import { getPrismaWithPostgresTx } from "../client"; | |
|
|
||
| interface GetWalletDetailsParams { | ||
| pgtx?: PrismaTransaction; | ||
| address: string; | ||
| walletAddress: string; | ||
| } | ||
|
|
||
| export class WalletDetailsError extends Error { | ||
|
|
@@ -130,6 +131,8 @@ export type SmartBackendWalletType = (typeof SmartBackendWalletTypes)[number]; | |
| export type BackendWalletType = (typeof BackendWalletTypes)[number]; | ||
| export type ParsedWalletDetails = z.infer<typeof walletDetailsSchema>; | ||
|
|
||
| export const walletDetailsCache = new LRUMap<string, ParsedWalletDetails>(2048); | ||
|
|
||
| /** | ||
| * Return the wallet details for the given address. | ||
| * | ||
|
|
@@ -143,20 +146,28 @@ export type ParsedWalletDetails = z.infer<typeof walletDetailsSchema>; | |
| */ | ||
| export const getWalletDetails = async ({ | ||
| pgtx, | ||
| address, | ||
| walletAddress: _walletAddress, | ||
| }: GetWalletDetailsParams) => { | ||
| // Wallet details are stored in lowercase. | ||
| const walletAddress = _walletAddress.toLowerCase(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ugh would love for all of us to align on lower vs checksum |
||
|
|
||
| const cachedDetails = walletDetailsCache.get(walletAddress); | ||
| if (cachedDetails) { | ||
| return cachedDetails; | ||
| } | ||
|
|
||
| const prisma = getPrismaWithPostgresTx(pgtx); | ||
| const config = await getConfig(); | ||
|
|
||
| const walletDetails = await prisma.walletDetails.findUnique({ | ||
| where: { | ||
| address: address.toLowerCase(), | ||
| address: walletAddress, | ||
| }, | ||
| }); | ||
|
|
||
| if (!walletDetails) { | ||
| throw new WalletDetailsError( | ||
| `No wallet details found for address ${address}`, | ||
| `No wallet details found for address ${walletAddress}`, | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -167,7 +178,7 @@ export const getWalletDetails = async ({ | |
| ) { | ||
| if (!walletDetails.awsKmsArn) { | ||
| throw new WalletDetailsError( | ||
| `AWS KMS ARN is missing for the wallet with address ${address}`, | ||
| `AWS KMS ARN is missing for the wallet with address ${walletAddress}`, | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -188,7 +199,7 @@ export const getWalletDetails = async ({ | |
| ) { | ||
| if (!walletDetails.gcpKmsResourcePath) { | ||
| throw new WalletDetailsError( | ||
| `GCP KMS resource path is missing for the wallet with address ${address}`, | ||
| `GCP KMS resource path is missing for the wallet with address ${walletAddress}`, | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -209,14 +220,17 @@ export const getWalletDetails = async ({ | |
|
|
||
| // zod schema can validate all necessary fields are populated after decryption | ||
| try { | ||
| return walletDetailsSchema.parse(walletDetails, { | ||
| const result = walletDetailsSchema.parse(walletDetails, { | ||
| errorMap: (issue) => { | ||
| const fieldName = issue.path.join("."); | ||
| return { | ||
| message: `${fieldName} is necessary for wallet ${address} of type ${walletDetails.type}, but not found in wallet details or configuration`, | ||
| message: `${fieldName} is necessary for wallet ${walletAddress} of type ${walletDetails.type}, but not found in wallet details or configuration`, | ||
| }; | ||
| }, | ||
| }); | ||
|
|
||
| walletDetailsCache.set(walletAddress, result); | ||
| return result; | ||
| } catch (e) { | ||
| if (e instanceof z.ZodError) { | ||
| throw new WalletDetailsError( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,9 @@ import { StatusCodes } from "http-status-codes"; | |
| import { randomUUID } from "node:crypto"; | ||
| import { TransactionDB } from "../../db/transactions/db"; | ||
| import { | ||
| ParsedWalletDetails, | ||
| getWalletDetails, | ||
| isSmartBackendWallet, | ||
| type ParsedWalletDetails, | ||
| } from "../../db/wallets/getWalletDetails"; | ||
| import { doesChainSupportService } from "../../lib/chain/chain-capabilities"; | ||
| import { createCustomError } from "../../server/middleware/error"; | ||
|
|
@@ -43,13 +43,40 @@ export const insertTransaction = async ( | |
| } | ||
| } | ||
|
|
||
| // Get wallet details. For EOA and SBW (v5 endpoints), `from` should return a valid backend wallet. | ||
| // For SBW (v4 endpoints), `accountAddress` should return a valid backend wallet. | ||
| // Else the provided details are incorrect (user error). | ||
| let walletDetails: ParsedWalletDetails | undefined; | ||
|
||
| let isSmartBackendWalletV4 = false; | ||
| try { | ||
| walletDetails = await getWalletDetails({ | ||
| walletAddress: insertedTransaction.from, | ||
| }); | ||
| } catch {} | ||
| if (!walletDetails && insertedTransaction.accountAddress) { | ||
| try { | ||
| walletDetails = await getWalletDetails({ | ||
| walletAddress: insertedTransaction.accountAddress, | ||
| }); | ||
| isSmartBackendWalletV4 = true; | ||
| } catch {} | ||
| } | ||
| if (!walletDetails) { | ||
| throw createCustomError( | ||
| "Account not found", | ||
| StatusCodes.BAD_REQUEST, | ||
| "ACCOUNT_NOT_FOUND", | ||
| ); | ||
| } | ||
|
|
||
| let queuedTransaction: QueuedTransaction = { | ||
| ...insertedTransaction, | ||
| status: "queued", | ||
| queueId, | ||
| queuedAt: new Date(), | ||
| resendCount: 0, | ||
|
|
||
| walletType: walletDetails.type, | ||
| from: getChecksumAddress(insertedTransaction.from), | ||
| to: getChecksumAddress(insertedTransaction.to), | ||
| signerAddress: getChecksumAddress(insertedTransaction.signerAddress), | ||
|
|
@@ -60,37 +87,34 @@ export const insertTransaction = async ( | |
| value: insertedTransaction.value ?? 0n, | ||
| }; | ||
|
|
||
| let walletDetails: ParsedWalletDetails | undefined; | ||
| // Handle smart backend wallets details. | ||
| if (isSmartBackendWallet(walletDetails)) { | ||
| if ( | ||
| !(await doesChainSupportService( | ||
| queuedTransaction.chainId, | ||
| "account-abstraction", | ||
| )) | ||
| ) { | ||
| throw createCustomError( | ||
| `Smart backend wallets do not support chain ${queuedTransaction.chainId}.`, | ||
| StatusCodes.BAD_REQUEST, | ||
| "INVALID_SMART_BACKEND_WALLET_TRANSACTION", | ||
| ); | ||
| } | ||
|
|
||
| try { | ||
| walletDetails = await getWalletDetails({ | ||
| address: queuedTransaction.from, | ||
| }); | ||
| queuedTransaction = { | ||
| ...queuedTransaction, | ||
| accountFactoryAddress: walletDetails.accountFactoryAddress ?? undefined, | ||
| entrypointAddress: walletDetails.entrypointAddress ?? undefined, | ||
| }; | ||
|
|
||
| // when using the v5 SDK with smart backend wallets, the following values are not set correctly: | ||
| // isUserOp is set to false | ||
| // account address is blank or the user provided value (this should be the SBW account address) | ||
| // from is set to the SBW account address (this should be the SBW signer address) | ||
| // these values need to be corrected so the worker can process the transaction | ||
| if (isSmartBackendWallet(walletDetails)) { | ||
| if (!isSmartBackendWalletV4) { | ||
| if (queuedTransaction.accountAddress) { | ||
| // Disallow smart backend wallets from sending userOps. | ||
| throw createCustomError( | ||
| "Smart backend wallets do not support interacting with other smart accounts", | ||
| "Smart backend wallets do not support sending transactions with other smart accounts", | ||
| StatusCodes.BAD_REQUEST, | ||
| "INVALID_SMART_BACKEND_WALLET_INTERACTION", | ||
| ); | ||
| } | ||
|
|
||
| if ( | ||
| !(await doesChainSupportService( | ||
| queuedTransaction.chainId, | ||
| "account-abstraction", | ||
| )) | ||
| ) { | ||
| throw createCustomError( | ||
| "Chain does not support smart backend wallets", | ||
| StatusCodes.BAD_REQUEST, | ||
| "SBW_CHAIN_NOT_SUPPORTED", | ||
| "INVALID_SMART_BACKEND_WALLET_TRANSACTION", | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -101,52 +125,8 @@ export const insertTransaction = async ( | |
| from: walletDetails.accountSignerAddress, | ||
| accountAddress: queuedTransaction.from, | ||
| target: queuedTransaction.to, | ||
| accountFactoryAddress: walletDetails.accountFactoryAddress ?? undefined, | ||
| entrypointAddress: walletDetails.entrypointAddress ?? undefined, | ||
| }; | ||
| } | ||
| } catch { | ||
| // if wallet details are not found, this is a smart backend wallet using a v4 endpoint | ||
| } | ||
|
|
||
| if (!walletDetails && queuedTransaction.accountAddress) { | ||
| try { | ||
| walletDetails = await getWalletDetails({ | ||
| address: queuedTransaction.accountAddress, | ||
| }); | ||
|
|
||
| // when using v4 SDK with smart backend wallets, the following values are not set correctly: | ||
| // entrypointAddress is not set | ||
| // accountFactoryAddress is not set | ||
| if (walletDetails && isSmartBackendWallet(walletDetails)) { | ||
| if ( | ||
| !(await doesChainSupportService( | ||
| queuedTransaction.chainId, | ||
| "account-abstraction", | ||
| )) | ||
| ) { | ||
| throw createCustomError( | ||
| "Chain does not support smart backend wallets", | ||
| StatusCodes.BAD_REQUEST, | ||
| "SBW_CHAIN_NOT_SUPPORTED", | ||
| ); | ||
| } | ||
|
|
||
| queuedTransaction = { | ||
| ...queuedTransaction, | ||
| entrypointAddress: walletDetails.entrypointAddress ?? undefined, | ||
| accountFactoryAddress: | ||
| walletDetails.accountFactoryAddress ?? undefined, | ||
| }; | ||
| } | ||
| } catch { | ||
| // if wallet details are not found for this either, this backend wallet does not exist at all | ||
| throw createCustomError( | ||
| "Account not found", | ||
| StatusCodes.BAD_REQUEST, | ||
| "ACCOUNT_NOT_FOUND", | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // Simulate the transaction. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.