Skip to content

PRO - 3097 - ARKA EPV08 Support - #198

Merged
vignesha22 merged 11 commits into
developfrom
PRO-3097-Arka_EPV08
Apr 17, 2025
Merged

PRO - 3097 - ARKA EPV08 Support#198
vignesha22 merged 11 commits into
developfrom
PRO-3097-Arka_EPV08

Conversation

@vignesha22

@vignesha22 vignesha22 commented Apr 2, 2025

Copy link
Copy Markdown
Collaborator

Description

  • Added EPV08 support

Types of changes

What types of changes does your code introduce?

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Other (please describe):

Further comments (optional)

Summary by CodeRabbit

  • New Features

    • Introduced EPV08 support, enabling enhanced paymaster interactions and improved smart contract operations.
    • Added a new deposit endpoint for more streamlined transaction processing.
    • Enhanced admin and whitelist functionalities to support multiple entry points.
  • Chores

    • Upgraded the package to version 3.2.0.
    • Improved network configurations and applied database updates to support extended data tracking.

@linear

linear Bot commented Apr 2, 2025

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Apr 2, 2025

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This 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

File(s) Change Summary
backend/CHANGELOG.md, backend/package.json Version bump to 3.2.0 and new EPV08 support entry added.
backend/config.json.default, backend/src/plugins/config.ts Added new chain ID configuration (11155111) and new EPV_08 config property from the environment.
backend/migrations/2025032800001-update-apiKey-table.cjs, backend/migrations/2025032800002-update-arka-whitelist-table.cjs New migration files adding VERIFYING_PAYMASTERS_V3 in api_keys and EP_VERSION in arka_whitelist tables.
backend/src/abi/VerifyingPaymasterAbiV3.ts Introduced new ABI file containing interface and bytecode for VerifyingPaymaster V3.
backend/src/constants/MultitokenPaymaster.ts Added token entry for ETH with 18 decimals and symbol 'ETH'.
backend/src/models/api-key.ts, backend/src/models/whitelist.ts Updated models by adding verifyingPaymastersV3 to APIKey and epVersion to ArkaWhitelist.
backend/src/paymaster/index.ts Added new method signV08 and updated constructor parameter from isEp06 to epVersion for enhanced entry point selection.
backend/src/repository/api-key-repository.ts, backend/src/repository/whitelist-repository.ts Modified repository methods to accept an epVersion parameter for version-specific logic.
backend/src/routes/admin-routes.ts, backend/src/routes/deposit-route.ts, backend/src/routes/metadata-routes.ts, backend/src/routes/paymaster-routes.ts, backend/src/routes/whitelist-routes.ts Updated endpoints and routing logic to support the new EPV08 entry point, including a new /deposit/v3 endpoint and additional whitelist routes.
backend/src/server.ts Added conditional checks for etherspotPaymasterAddress before deposit processing.
backend/src/types/sponsorship-policy-dto.ts, backend/src/types/whitelist-dto.ts Extended type definitions with new EPV_08 in the enum and an optional epVersion property.

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
Loading
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
Loading

Poem

I'm a bouncy bunny, coding in the glen,
Leaping through commits with joy again and again.
EPV08 hops in with a swift, clever twist,
Upgrading endpoints—how can one resist?
From migration to model, the changes do shine,
A rabbit's delight in this code upgrade divine!
🐇🌟


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vignesha22 vignesha22 changed the title PRO - 3097 - ARKA epv08 Support PRO - 3097 - ARKA EPV08 Support Apr 2, 2025
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Apr 2, 2025

Copy link
Copy Markdown

Deploying arka with  Cloudflare Pages  Cloudflare Pages

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

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 265596f and 032d8fd.

📒 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 parameter

The addition of an optional epVersion string parameter to the WhitelistDto interface properly supports the EPV08 feature while maintaining backward compatibility for existing code.

backend/package.json (1)

3-3: Version update correctly reflects feature addition

The 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 entrypoints

The 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 feature

The 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 adding VERIFYING_PAYMASTERS_V3 column

The 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 entry

The 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 column

The 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 paymasters

The new property is correctly typed and follows the established naming conventions.


70-74: LGTM: Properly configured model field for database mapping

The 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 etherspotPaymasterAddress property of network.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 testnet

The 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 import

The 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 epVersion

The 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_ENTRYPOINTS

The 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 intentional

Unlike the v1 and v2 endpoints, the v3 endpoint doesn't check for a useVp parameter and directly tries to use verifyingPaymastersV3. 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 typescript

Length 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 of updateVpAddresses has changed, please ensure every call to it passes the proper EPVersion. Note that our initial search with --type typescript failed 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.
Using EPVersions will 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.

Comment thread backend/src/routes/metadata-routes.ts
Comment thread backend/src/server.ts Outdated
Comment thread backend/src/repository/whitelist-repository.ts
Comment thread backend/src/routes/deposit-route.ts
Comment thread backend/src/repository/api-key-repository.ts Outdated
Comment on lines +569 to 579
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 })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment on lines +652 to +741
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 })
}
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ 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.

Comment on lines +742 to +835
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 })
}
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment on lines +836 to +901
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 })
}
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment on lines +903 to +961
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 })
}
} )

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Comment thread backend/src/paymaster/index.ts Outdated
Comment thread backend/src/paymaster/index.ts Outdated
Comment thread backend/src/paymaster/index.ts Outdated
Comment thread backend/src/paymaster/index.ts Outdated
Comment thread backend/src/plugins/config.ts
Comment thread backend/src/routes/paymaster-routes.ts
Comment thread backend/src/routes/whitelist-routes.ts
Comment thread backend/src/routes/whitelist-routes.ts
Comment thread backend/src/server.ts Outdated
Comment thread backend/src/types/whitelist-dto.ts
@vignesha22
vignesha22 changed the base branch from master to develop April 7, 2025 11:17

@ch4r10t33r ch4r10t33r left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@vignesha22
vignesha22 merged commit 5722fde into develop Apr 17, 2025
@vignesha22
vignesha22 deleted the PRO-3097-Arka_EPV08 branch April 17, 2025 12:15
@coderabbitai coderabbitai Bot mentioned this pull request Apr 24, 2025
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants