PRO - 3097 - ARKA EPV08 Support - #198
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis update introduces version 3.2.0 with a new EPV08 feature, reflected in the changelog and package version bump. New configuration settings for chain ID 11155111 and the EPV_08 environment variable have been added. Two migration scripts have been implemented to add columns in the API keys and whitelist tables. A new ABI for VerifyingPaymaster V3 and a token entry for ETH are included. Key models, repository methods, and routes have been updated to support EPV08, with new endpoints for deposit and whitelist operations. The paymaster class now features a signV08 method, and type definitions have been extended accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant S as Server (/deposit/v3)
participant R as APIKeyRepository
participant P as Paymaster (signV08)
participant D as Provider/Contract
C->>S: POST /deposit/v3 (api_key, amount, chainId)
S->>R: Retrieve API Key entity
R-->>S: API Key Data
S->>P: Invoke signV08(userOp, epVersion, ...)
P->>D: Setup provider & contract, process signing
D-->>P: Return signing result
P-->>S: Return signed operation
S->>C: Send deposit response
sequenceDiagram
participant C as Client
participant S as Server (/whitelist/v3)
participant W as WhitelistRepository
C->>S: POST /whitelist/v3 (api_key, address, epVersion, policyId)
S->>W: Query findOneByApiKeyEPVersionAndPolicyId(...)
W-->>S: Whitelist record / null
S->>W: Create/Update whitelist record
W-->>S: Operation result
S->>C: Send whitelist update response
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Deploying arka with
|
| Latest commit: |
3d71167
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://5b2a5093.arka-3qg.pages.dev |
| Branch Preview URL: | https://pro-3097-arka-epv08.arka-3qg.pages.dev |
There was a problem hiding this comment.
Actionable comments posted: 17
🔭 Outside diff range comments (1)
backend/src/abi/VerifyingPaymasterAbiV3.ts (1)
1-316: 🧹 Nitpick (assertive)Storing ABI and bytecode in code can hamper maintainability.
Embedding the entire ABI/bytecode in the TypeScript code is convenient but might hinder maintainability if the contract changes frequently. Consider referencing a compiled artifact as a JSON file or generating it automatically during deployment to minimize the risk of mismatches.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (21)
backend/CHANGELOG.md(1 hunks)backend/config.json.default(1 hunks)backend/migrations/2025032800001-update-apiKey-table.cjs(1 hunks)backend/migrations/2025032800002-update-arka-whitelist-table.cjs(1 hunks)backend/package.json(1 hunks)backend/src/abi/VerifyingPaymasterAbiV3.ts(1 hunks)backend/src/constants/MultitokenPaymaster.ts(1 hunks)backend/src/models/api-key.ts(2 hunks)backend/src/models/whitelist.ts(2 hunks)backend/src/paymaster/index.ts(4 hunks)backend/src/plugins/config.ts(3 hunks)backend/src/repository/api-key-repository.ts(2 hunks)backend/src/repository/whitelist-repository.ts(3 hunks)backend/src/routes/admin-routes.ts(6 hunks)backend/src/routes/deposit-route.ts(2 hunks)backend/src/routes/metadata-routes.ts(1 hunks)backend/src/routes/paymaster-routes.ts(6 hunks)backend/src/routes/whitelist-routes.ts(6 hunks)backend/src/server.ts(2 hunks)backend/src/types/sponsorship-policy-dto.ts(3 hunks)backend/src/types/whitelist-dto.ts(1 hunks)
🧰 Additional context used
🧬 Code Definitions (3)
backend/src/server.ts (1)
backend/src/utils/monitorTokenPaymaster.ts (1)
checkDeposit(6-22)
backend/src/repository/whitelist-repository.ts (1)
backend/src/models/whitelist.ts (1)
ArkaWhitelist(3-11)
backend/src/routes/whitelist-routes.ts (4)
backend/src/paymaster/index.ts (1)
Paymaster(52-1267)backend/src/utils/common.ts (2)
printRequest(6-10)getNetworkConfig(12-23)backend/src/models/api-key.ts (1)
APIKey(3-24)backend/src/utils/crypto.ts (1)
decode(31-43)
🪛 markdownlint-cli2 (0.17.2)
backend/CHANGELOG.md
2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above
(MD022, blanks-around-headings)
2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above
(MD022, blanks-around-headings)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
4-4: Lists should be surrounded by blank lines
null
(MD032, blanks-around-lists)
🪛 Biome (1.9.4)
backend/src/paymaster/index.ts
[error] 253-253: Template literals are preferred over string concatenation.
Unsafe fix: Use a template literal.
(lint/style/useTemplate)
backend/src/routes/admin-routes.ts
[error] 395-395: Declare variables separately
Unsafe fix: Break out into multiple declarations
(lint/style/useSingleVarDeclarator)
[error] 502-502: Declare variables separately
Unsafe fix: Break out into multiple declarations
(lint/style/useSingleVarDeclarator)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Cloudflare Pages
🔇 Additional comments (32)
backend/src/types/whitelist-dto.ts (1)
5-5: Clean addition of EPV support parameterThe addition of an optional
epVersionstring parameter to theWhitelistDtointerface properly supports the EPV08 feature while maintaining backward compatibility for existing code.backend/package.json (1)
3-3: Version update correctly reflects feature additionThe version bump from 3.1.5 to 3.2.0 follows semantic versioning principles, where the minor version increment (3.1.x → 3.2.0) correctly indicates the addition of a new feature (EPV08 support) without breaking changes.
backend/src/routes/metadata-routes.ts (1)
21-22: EPV_08 properly added to supported entrypointsThe addition of EPV_08 to the SUPPORTED_ENTRYPOINTS object is correctly implemented, allowing the server configuration to reference the new entry point version alongside the existing EPV_06 and EPV_07.
backend/CHANGELOG.md (2)
2-5: Changelog correctly documents new featureThe changelog entry properly follows the established format and clearly indicates the addition of EPV08 Support in version 3.2.0.
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above(MD022, blanks-around-headings)
2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below(MD022, blanks-around-headings)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above(MD022, blanks-around-headings)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below(MD022, blanks-around-headings)
4-4: Lists should be surrounded by blank lines
null(MD032, blanks-around-lists)
2-5:Details
❓ Verification inconclusive
Fix markdown formatting issues
The markdown formatting for the changelog has some issues. Headings and lists should be surrounded by blank lines according to standard markdown conventions.
# Changelog + ## [3.2.0] - 2025-04-01 + ### New + - Added EPV08 Support ## [3.1.5] - 2025-03-24
🏁 Script executed:
#!/bin/bash # Check if there are any markdown linting rules in the project fd -t f "\.markdownlint" -t f "\.markdownrc" -t f "markdownlint.config.js"Length of output: 243
I'll generate a new shell script that searches for any markdown lint configuration files using a more flexible file search and then displays the contents of
backend/CHANGELOG.md. This will help verify if any markdown linting rules are in force and confirm the current formatting of the changelog. Please run the script and share the output for manual verification.#!/bin/bash echo "Searching for markdown lint configuration files..." find . -type f \( -iname ".markdownlint*" -o -iname "*markdownrc" -o -iname "markdownlint.config.js" \) echo "" echo "Displaying backend/CHANGELOG.md content:" cat backend/CHANGELOG.md🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above(MD022, blanks-around-headings)
2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below(MD022, blanks-around-headings)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above(MD022, blanks-around-headings)
3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below(MD022, blanks-around-headings)
4-4: Lists should be surrounded by blank lines
null(MD032, blanks-around-lists)
backend/migrations/2025032800001-update-apiKey-table.cjs (1)
1-27: LGTM: Well-structured migration file for addingVERIFYING_PAYMASTERS_V3columnThe migration correctly adds a new column to the api_keys table with appropriate null handling, consistent with migration best practices.
backend/src/constants/MultitokenPaymaster.ts (1)
192-193: Good addition of ETH token entryThe ETH token configuration follows the project's pattern, using the standard convention address for native ETH (
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) and setting the correct 18 decimals.backend/migrations/2025032800002-update-arka-whitelist-table.cjs (1)
1-27: LGTM: Clean migration for adding EP_VERSION columnThe migration properly adds the EP_VERSION column to support the new EPV08 feature, allowing for null values which is appropriate for a schema update.
backend/src/models/api-key.ts (2)
12-12: LGTM: Property addition for new verifying paymastersThe new property is correctly typed and follows the established naming conventions.
70-74: LGTM: Properly configured model field for database mappingThe field configuration correctly maps to the new database column, maintaining consistency with similar fields in the model.
backend/src/models/whitelist.ts (2)
8-8: LGTM: Added new epVersion property to the ArkaWhitelist class.The addition of an optional epVersion property that can be null (defaulting to null) is well-structured and maintains backward compatibility with existing data.
38-42: LGTM: Field mapping for epVersion follows the established pattern.The database field mapping for the epVersion property is correctly implemented, using consistent naming conventions and allowing null values to match the class definition.
backend/src/server.ts (1)
310-312: LGTM: Added proper null check for etherspotPaymasterAddress.The optional chaining operator is correctly used here to safely access the
etherspotPaymasterAddressproperty ofnetwork.contracts.backend/src/plugins/config.ts (3)
32-32: LGTM: Added EPV_08 to ConfigSchema.The schema definition for EPV_08 as an array of strings is consistent with how other entrypoint versions are defined.
75-75: LGTM: Added environment variable processing for EPV_08.The code correctly reads the EPV_08 environment variable and splits it by commas, maintaining consistency with other entrypoint version configurations.
114-114: LGTM: Added default value for EPV_08.The default value for EPV_08 is set to
['0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108'], following the same pattern used for other entrypoint versions.backend/src/types/sponsorship-policy-dto.ts (3)
38-38: LGTM: Added EPV_08 to EPVersions enum.The EPV_08 value has been properly added to the EPVersions enum, maintaining the established pattern.
51-52: LGTM: Updated getEPVersion function to support EPV_08.The getEPVersion function has been correctly updated to handle the new EPV_08 version string.
65-66: LGTM: Updated getEPVersionString function to support EPV_08.The getEPVersionString function has been properly updated to return the string representation for EPV_08.
backend/config.json.default (1)
472-480: Excellent addition of EPV08 support for Sepolia testnetThe new configuration entry for chain ID 11155111 (Sepolia) with the v3 bundler URL and the new entry point address follows the established pattern in the file. This properly sets up the infrastructure for EPV08 support, which aligns with the PR objectives.
backend/src/repository/whitelist-repository.ts (2)
4-4: Good addition of EPVersions importThe import of EPVersions from the sponsorship-policy-dto.js file is appropriate for the new functionality.
18-19: LGTM - Properly handling null values for policyId and epVersionThe create method correctly handles the case where policyId or epVersion might be null by using the nullish coalescing operator.
backend/src/routes/deposit-route.ts (2)
17-20: Good addition of EPV_08 to SUPPORTED_ENTRYPOINTSThe update to include EPV_08 in the SUPPORTED_ENTRYPOINTS object is necessary for the new functionality.
237-243: Verify that relying only on verifyingPaymastersV3 is intentionalUnlike the v1 and v2 endpoints, the v3 endpoint doesn't check for a
useVpparameter and directly tries to useverifyingPaymastersV3. This is a behavior change that should be intentional.Can you confirm that this behavior change is intentional and documented in your team's release notes? Also, can you confirm that all anticipated users of the v3 endpoint will have a deployed verifying paymaster and will understand this requirement?
backend/src/repository/api-key-repository.ts (1)
64-71:Details
❓ Verification inconclusive
Verify that all callers have been updated
Since the method signature has changed, it's important to ensure that all callers of this method have been updated to provide the correct EPVersion parameter.
🏁 Script executed:
#!/bin/bash # Find all places where updateVpAddresses is called rg "updateVpAddresses\(" --type typescriptLength of output: 79
Below is a revised review comment that requests further manual verification and provides a corrected search script:
Action Required: Verify All Callers Supply the Correct EPVersion Parameter
Since the method signature ofupdateVpAddresseshas changed, please ensure every call to it passes the proper EPVersion. Note that our initial search with--type typescriptfailed due to an unrecognized file type. Use the corrected command below to locate all caller sites:#!/bin/bash # Find all invocations of updateVpAddresses in TypeScript files rg "updateVpAddresses\(" -g "*.ts"After running this command, review the results manually to confirm that all callers have been updated accordingly.
backend/src/routes/admin-routes.ts (2)
35-36: Looks good adding EPV_07 and EPV_08 to the SUPPORTED_ENTRYPOINTS.The extended coverage for EPV_08 is clear and helps maintain consistent logic for all entry points.
388-390: Good job checking all valid EP versions.This ensures that only recognized versions (EPV_06, EPV_07, EPV_08) can pass through, preventing invalid entry points from proceeding.
backend/src/paymaster/index.ts (1)
24-25: Importing new ABI and EPVersions looks good.Including the V3 code references and enumeration expands support for EPV_08 effectively.
backend/src/routes/paymaster-routes.ts (2)
23-24: Welcome addition of EPV_08.These lines ensure the new entry point version is recognized and allowed.
93-96: Solid extension of epVersion checks.Accepting EPV_06, EPV_07, and EPV_08 maintains consistency with the new feature.
backend/src/routes/whitelist-routes.ts (2)
13-13: No concerns on the new import statement.
UsingEPVersionswill help keep version strings consistent with the rest of the codebase.
20-21: Good job adding the new EPV_08 version.
Ensures that the new entry point version is recognized and used across the codebase.
| const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId); | ||
| const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId); | ||
|
|
||
| if (!existingWhitelistRecord) { | ||
| if (!existingWhitelistRecord && !existingGlobalWhitelistRecord) { | ||
| throw new Error(ErrorMessage.NO_WHITELIST_FOUND); | ||
| } | ||
| const result = { message: existingWhitelistRecord.addresses.includes(accountAddress) ? 'Already added' : 'Not added yet' } | ||
| const combinedRecord = { addresses: [...(existingWhitelistRecord?.addresses || []), ...(existingGlobalWhitelistRecord?.addresses || [])] } | ||
| const result = { message: combinedRecord.addresses.includes(accountAddress) ? 'Already added' : 'Not added yet' } | ||
| server.log.info(result, 'Response sent: '); | ||
| if (body.jsonrpc) | ||
| return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Unified retrieval of global and version-specific records.
This approach effectively checks both records to identify membership, but repeated logic across routes could lead to maintenance overhead. Consider extracting the retrieval and combination logic into a helper function or repository method to improve readability and reduce duplication.
| server.post("/whitelist/v3", | ||
| async function (request, reply) { | ||
| try { | ||
| printRequest("/whitelist/v3", request, server.log); | ||
| const body: any = request.body; | ||
| const query: any = request.query; | ||
| if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS }); | ||
| const address = body.params?.[0]; | ||
| const policyId = body.params?.[1]; | ||
| const api_key = query['apiKey'] ?? body.params?.[2]; | ||
| if (!api_key || typeof(api_key) !== "string") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| let privateKey = ''; | ||
| const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(api_key); | ||
| if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if (!unsafeMode) { | ||
| const AWSresponse = await client.send( | ||
| new GetSecretValueCommand({ | ||
| SecretId: prefixSecretId + api_key, | ||
| }) | ||
| ); | ||
| const secrets = JSON.parse(AWSresponse.SecretString ?? '{}'); | ||
| if (!secrets['PRIVATE_KEY']) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| privateKey = secrets['PRIVATE_KEY']; | ||
| } else { | ||
| privateKey = decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET); | ||
| } | ||
| if (!privateKey) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if ( | ||
| !Array.isArray(address) || | ||
| address.length > 10 | ||
| ) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); | ||
| } | ||
| if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| } | ||
| const validAddresses = address.every(ethers.utils.isAddress); | ||
| if (!validAddresses) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_ADDRESS_PASSSED }); | ||
| const signer = new Wallet(privateKey) | ||
| if (policyId) { | ||
| const policyRecord = await server.sponsorshipPolicyRepository.findOneById(policyId); | ||
| if (!policyRecord || (policyRecord?.walletAddress !== signer.address)) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_SPONSORSHIP_POLICY_ID }) | ||
| } | ||
|
|
||
| const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_08, policyId); | ||
| const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId); | ||
|
|
||
| if (existingWhitelistRecord || existingGlobalWhitelistRecord) { | ||
| const toBeAdded: string[] = []; | ||
| address.filter(ele => { | ||
| if (!existingWhitelistRecord?.addresses.includes(ele) && !existingGlobalWhitelistRecord?.addresses.includes(ele)) toBeAdded.push(ele); | ||
| }); | ||
| if (toBeAdded.length < 1) return reply.code(ReturnCode.CONFLICT).send({ error: ErrorMessage.ADDRESS_ALREADY_ADDED }); | ||
| if (!existingWhitelistRecord) { | ||
| const addWhitelistDto = { | ||
| apiKey: api_key, | ||
| addresses: toBeAdded, | ||
| policyId: policyId ?? null, | ||
| epVersion: EPVersions.EPV_08, | ||
| } | ||
| await server.whitelistRepository.create(addWhitelistDto); | ||
| } else { | ||
| const allAddresses = toBeAdded.concat(existingWhitelistRecord.addresses); | ||
| existingWhitelistRecord.addresses = allAddresses; | ||
| await server.whitelistRepository.updateOneById(existingWhitelistRecord); | ||
| } | ||
| } else { | ||
| const addWhitelistDto = { | ||
| apiKey: api_key, | ||
| addresses: address, | ||
| policyId: policyId ?? null, | ||
| epVersion: EPVersions.EPV_08, | ||
| } | ||
| await server.whitelistRepository.create(addWhitelistDto); | ||
| } | ||
| const result = { message: "Successfully whitelisted" } | ||
| server.log.info(result, 'Response sent: '); | ||
| if (body.jsonrpc) | ||
| return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) | ||
| return reply.code(ReturnCode.SUCCESS).send(result); | ||
| } catch (err: any) { | ||
| request.log.error(err); | ||
| if (err.name == "ResourceNotFoundException") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }); | ||
| return reply.code(ReturnCode.FAILURE).send({ error: err.message ?? ErrorMessage.FAILED_TO_PROCESS }) | ||
| } | ||
| } | ||
| ); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Duplicated logic in v3 route.
The new /whitelist/v3 route heavily mirrors the v2 logic. To avoid code duplication and potential maintenance issues, consider factoring out the shared logic (checking global vs. version-specific records, merging addresses) into a reusable method or service layer.
| server.post("/removeWhitelist/v3", | ||
| async function (request, reply) { | ||
| try { | ||
| printRequest("/removeWhitelist/v3", request, server.log); | ||
| const body: any = request.body; | ||
| const query: any = request.query; | ||
| if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS }); | ||
| const address = body.params?.[0]; | ||
| const policyId = body.params?.[1]; | ||
| const chainId = query['chainId'] ?? body.params?.[2]; | ||
| const api_key = query['apiKey'] ?? body.params?.[3]; | ||
| if (!api_key || typeof(api_key) !== "string") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| let privateKey = ''; | ||
| const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(api_key); | ||
| if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if (!unsafeMode) { | ||
| const AWSresponse = await client.send( | ||
| new GetSecretValueCommand({ | ||
| SecretId: prefixSecretId + api_key, | ||
| }) | ||
| ); | ||
| const secrets = JSON.parse(AWSresponse.SecretString ?? '{}'); | ||
| if (!secrets['PRIVATE_KEY']) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| privateKey = secrets['PRIVATE_KEY']; | ||
| } else { | ||
| privateKey = decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET); | ||
| } | ||
| const supportedNetworks = apiKeyEntity.supportedNetworks; | ||
| if (!privateKey) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if ( | ||
| !Array.isArray(address) || | ||
| address.length > 10 || | ||
| !chainId || | ||
| isNaN(chainId) | ||
| ) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); | ||
| } | ||
| if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| } | ||
| const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_08); | ||
| if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| const validAddresses = address.every(ethers.utils.isAddress); | ||
| if (!validAddresses) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_ADDRESS_PASSSED }); | ||
| const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_08, policyId); | ||
| const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId); | ||
| let isGlobal = false; | ||
|
|
||
| if (existingWhitelistRecord || existingGlobalWhitelistRecord) { | ||
| const toBeRemoved: string[] = []; | ||
| address.filter(ele => { | ||
| if (existingWhitelistRecord?.addresses.includes(ele)) { | ||
| toBeRemoved.push(ele); | ||
| existingWhitelistRecord.addresses.splice(existingWhitelistRecord.addresses.indexOf(ele), 1); | ||
| } | ||
| if (existingGlobalWhitelistRecord?.addresses.includes(ele)) { | ||
| toBeRemoved.push(ele); | ||
| existingGlobalWhitelistRecord.addresses.splice(existingGlobalWhitelistRecord.addresses.indexOf(ele), 1); | ||
| isGlobal = true; | ||
| } | ||
| }); | ||
| if (toBeRemoved.length < 1) return reply.code(ReturnCode.CONFLICT).send({ error: ErrorMessage.ADDRESS_NOT_WHITELISTED }); | ||
|
|
||
| if (isGlobal) { | ||
| if (existingGlobalWhitelistRecord?.addresses.length == 0) { | ||
| await server.whitelistRepository.deleteById(existingGlobalWhitelistRecord.id); | ||
| } else if (existingGlobalWhitelistRecord){ | ||
| await server.whitelistRepository.updateOneById(existingGlobalWhitelistRecord); | ||
| } | ||
| } else { | ||
| if (existingWhitelistRecord?.addresses.length == 0) { | ||
| await server.whitelistRepository.deleteById(existingWhitelistRecord.id); | ||
| } else if (existingWhitelistRecord) { | ||
| await server.whitelistRepository.updateOneById(existingWhitelistRecord); | ||
| } | ||
| } | ||
| } else { | ||
| throw new Error(ErrorMessage.NO_WHITELIST_FOUND); | ||
| } | ||
| const result = { message: "Successfully removed whitelisted addresses" } | ||
| server.log.info(result, 'Response sent: '); | ||
| if (body.jsonrpc) | ||
| return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) | ||
| return reply.code(ReturnCode.SUCCESS).send(result); | ||
| } catch (err: any) { | ||
| request.log.error(err); | ||
| if (err.name == "ResourceNotFoundException") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }); | ||
| return reply.code(ReturnCode.FAILURE).send({ error: err.message ?? ErrorMessage.FAILED_TO_PROCESS }) | ||
| } | ||
| } | ||
| ); | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Removal logic repeated.
The new removeWhitelist/v3 route repeats much of the same logic from v2, specifically for EPV_08. Centralizing this logic in a shared function would reduce redundancy. Additionally, consider concurrency controls for removing addresses to ensure atomic updates.
| server.post("/checkWhitelist/v3", | ||
| async function (request, reply) { | ||
| try { | ||
| printRequest("/checkWhitelist/v3", request, server.log); | ||
| const body: any = request.body; | ||
| const query: any = request.query; | ||
| if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS }); | ||
| const accountAddress = body.params?.[0]; | ||
| const policyId = body.params?.[1]; | ||
| const chainId = query['chainId'] ?? body.params?.[2]; | ||
| const api_key = query['apiKey'] ?? body.params?.[3]; | ||
| if (!accountAddress || !ethers.utils.isAddress(accountAddress)) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); | ||
| } | ||
| if (!api_key || typeof(api_key) !== "string") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| let privateKey = ''; | ||
| const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(api_key); | ||
| if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if (!unsafeMode) { | ||
| const AWSresponse = await client.send( | ||
| new GetSecretValueCommand({ | ||
| SecretId: prefixSecretId + api_key, | ||
| }) | ||
| ); | ||
| const secrets = JSON.parse(AWSresponse.SecretString ?? '{}'); | ||
| if (!secrets['PRIVATE_KEY']) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| privateKey = secrets['PRIVATE_KEY']; | ||
| } else { | ||
| privateKey = decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET); | ||
| } | ||
| const supportedNetworks = apiKeyEntity.supportedNetworks; | ||
| if (!privateKey) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if ( | ||
| !accountAddress || | ||
| !ethers.utils.isAddress(accountAddress) || | ||
| !chainId || | ||
| isNaN(chainId) | ||
| ) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); | ||
| } | ||
| if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| } | ||
| const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_08); | ||
| if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_08, policyId); | ||
| const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId); | ||
|
|
||
| if (!existingWhitelistRecord && !existingGlobalWhitelistRecord) { | ||
| throw new Error(ErrorMessage.NO_WHITELIST_FOUND); | ||
| } | ||
| const combinedRecord = { addresses: [...(existingWhitelistRecord?.addresses || []), ...(existingGlobalWhitelistRecord?.addresses || [])] } | ||
| const result = { message: combinedRecord.addresses.includes(accountAddress) ? 'Already added' : 'Not added yet' } | ||
| server.log.info(result, 'Response sent: '); | ||
| if (body.jsonrpc) | ||
| return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) | ||
| return reply.code(ReturnCode.SUCCESS).send(result); | ||
| } catch (err: any) { | ||
| request.log.error(err); | ||
| if (err.name == "ResourceNotFoundException") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }); | ||
| return reply.code(ReturnCode.FAILURE).send({ error: err.message ?? ErrorMessage.FAILED_TO_PROCESS }) | ||
| } | ||
| } | ||
| ); |
There was a problem hiding this comment.
🧹 Nitpick (assertive)
Again, consider code reuse for checking whitelisted addresses.
This route duplicates the approach used by the v2 variant, indicating a pattern that might be factored out for maintainability. A single function could fetch both version-specific and global lists, combine them, and return whether a user is whitelisted.
| server.post("/getAllWhitelist/v3", | ||
| async function (request, reply) { | ||
| try { | ||
| printRequest("/getAllWhitelist/v3", request, server.log); | ||
| const body: any = request.body; | ||
| const query: any = request.query; | ||
| const policyId = body?.params?.[0]; | ||
| const chainId = query['chainId'] ?? body?.params?.[1]; | ||
| const api_key = query['apiKey'] ?? body?.params?.[2]; | ||
| if (!api_key || typeof(api_key) !== "string") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| let privateKey = ''; | ||
| const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(api_key); | ||
| if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| if (!unsafeMode) { | ||
| const AWSresponse = await client.send( | ||
| new GetSecretValueCommand({ | ||
| SecretId: prefixSecretId + api_key, | ||
| }) | ||
| ); | ||
| const secrets = JSON.parse(AWSresponse.SecretString ?? '{}'); | ||
| if (!secrets['PRIVATE_KEY']) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| privateKey = secrets['PRIVATE_KEY']; | ||
| } else { | ||
| privateKey = decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET); | ||
| } | ||
| if (!privateKey) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) | ||
| const supportedNetworks = apiKeyEntity.supportedNetworks; | ||
| if ( | ||
| !chainId || | ||
| isNaN(chainId) | ||
| ) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); | ||
| } | ||
| if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) { | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| } | ||
| const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_08); | ||
| if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); | ||
| const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId); | ||
| console.log(existingGlobalWhitelistRecord, 'existingGlobalWhitelistRecord') | ||
| const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_08, policyId); | ||
| console.log(existingWhitelistRecord, 'existingWhitelistRecord') | ||
| if (!existingWhitelistRecord && !existingGlobalWhitelistRecord) { | ||
| throw new Error(ErrorMessage.NO_WHITELIST_FOUND); | ||
| } | ||
| const result = { addresses: [...(existingWhitelistRecord?.addresses || []), ...(existingGlobalWhitelistRecord?.addresses || [])] } | ||
| server.log.info(result, 'Response sent: '); | ||
| if (body.jsonrpc) | ||
| return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) | ||
| return reply.code(ReturnCode.SUCCESS).send(result); | ||
| } catch (err: any) { | ||
| request.log.error(err); | ||
| if (err.name == "ResourceNotFoundException") | ||
| return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }); | ||
| return reply.code(ReturnCode.FAILURE).send({ error: err.message ?? ErrorMessage.FAILED_TO_PROCESS }) | ||
| } | ||
| } ) | ||
|
|
There was a problem hiding this comment.
Potential bug in array push usage.
Be aware that Array.prototype.push() returns the new length of the array rather than the updated array. This can cause result.addresses to store a number instead of the combined addresses if you use the assignment operator with push(...existingEpWhitelistRecord.addresses). Consider using concat() or reassigning the array after pushing.
Description
Types of changes
What types of changes does your code introduce?
Further comments (optional)
Summary by CodeRabbit
New Features
Chores