Skip to content

ft: adding endpoints for withdrawing stake and deposit#234

Merged
nikhilkumar1612 merged 2 commits intomasterfrom
ft-withdraw
Apr 5, 2026
Merged

ft: adding endpoints for withdrawing stake and deposit#234
nikhilkumar1612 merged 2 commits intomasterfrom
ft-withdraw

Conversation

@nikhilkumar1612
Copy link
Copy Markdown
Member

@nikhilkumar1612 nikhilkumar1612 commented Apr 5, 2026

Description

  • adding endpoints for sithdrawing stake and deposit

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

    • Admin stake withdrawal endpoint with address validation.
    • Deposit withdrawal endpoints for multiple entrypoint versions (v0.6, v0.7, v0.8).
    • Paymaster operations to submit stake and deposit withdrawals, returning transaction results.
  • Bug Fixes / Errors

    • Added specific error messages for invalid withdraw addresses and failed withdraw operations.
  • Documentation

    • Changelog updated for release 4.2.3.
  • Chores

    • Backend package version bumped to 4.2.3.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 5, 2026

Walkthrough

Added withdrawal functionality: three new error messages; two Paymaster methods (withdrawDeposit, withdrawStake) that construct and submit transactions with gas fee logic and type selection; new admin and deposit HTTP routes that validate requests, resolve credentials, and invoke the Paymaster withdraw methods.

Changes

Cohort / File(s) Summary
Error Messages
backend/src/constants/ErrorMessage.ts
Added INVALID_WITHDRAW_ADDRESS, FAILED_TO_WITHDRAW_STAKE, and FAILED_TO_WITHDRAW_DEPOSIT.
Paymaster
backend/src/paymaster/index.ts
Added withdrawDeposit(...) and withdrawStake(...) methods. Each builds and submits withdraw transactions, computes gas via getGasFee/fallback, selects legacy vs EIP-1559 tx type, and throws specific error messages on failure.
Admin Routes
backend/src/routes/admin-routes.ts
Added POST /withdrawStake endpoint with request/body validation (chainId, apiKey, epVersion, withdrawAddress via isAddress), API key lookup, credential/bundler resolution, and call to paymaster.withdrawStake().
Deposit Routes
backend/src/routes/deposit-route.ts
Added withdrawDeposit handler and three POST routes (/withdrawDeposit, /withdrawDeposit/v2, /withdrawDeposit/v3). Validates withdrawAddress, withdrawAmount, chainId, resolves credentials, selects paymaster by epVersion, and calls paymaster.withdrawDeposit().
Changelog & Version
backend/CHANGELOG.md, backend/package.json
Bumped package version to 4.2.3 and added changelog entry mentioning new withdraw endpoints.

Sequence Diagram

sequenceDiagram
    participant Client
    participant RouteHandler as Route Handler
    participant Paymaster
    participant FeeResolver as Fee Resolver
    participant Blockchain

    Client->>RouteHandler: POST /withdrawDeposit (address, amount, chainId, apiKey)
    RouteHandler->>RouteHandler: Validate params & epVersion, isAddress(address)
    RouteHandler->>RouteHandler: Resolve credentials (AWS Secrets or unsafeMode)
    RouteHandler->>RouteHandler: Select paymaster address (by epVersion & chainId)
    RouteHandler->>Paymaster: withdrawDeposit(address, amount, paymasterAddr, bundlerRpc, key)

    Paymaster->>FeeResolver: getGasFee() or publicClient.getGasPrice()
    FeeResolver-->>Paymaster: gas params
    Paymaster->>Paymaster: Choose tx type (legacy vs EIP-1559)
    Paymaster->>Blockchain: submitTx(withdraw call, tx params)
    Blockchain-->>Paymaster: txHash / receipt

    Paymaster-->>RouteHandler: { success, txHash }
    RouteHandler-->>Client: 200 OK { code, data: { txHash } }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • ch4r10t33r
  • kanthgithub

Poem

🐰
I hop where ledgers softly hum,
I pull the stake, I fetch the sum,
Fee math jiggles, txs take flight,
Coins bounce home by moonlit byte,
Hooray — withdrawals done just right!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main changes: adding endpoints for withdrawing stake and deposit. It is clear, specific, and accurately reflects the primary objective of the changeset.
Description check ✅ Passed The description contains a typo ('sithdrawing' instead of 'withdrawing'), the detailed description is minimal, and further comments are absent. However, the required type-of-changes section is properly filled with 'New feature' selected, making the description mostly complete despite the typo and brevity.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ft-withdraw

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 5, 2026

Deploying arka with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0f3d1d0
Status: ✅  Deploy successful!
Preview URL: https://e3246a78.arka-3qg.pages.dev
Branch Preview URL: https://ft-withdraw.arka-3qg.pages.dev

View logs

Copy link
Copy Markdown
Member

@ch4r10t33r ch4r10t33r left a comment

Choose a reason for hiding this comment

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

Looks good

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/paymaster/index.ts`:
- Around line 1478-1498: The gas-fee check for deciding legacy vs EIP-1559 is
missing the zero-check and can produce EIP-1559 txns with maxFeePerGas === 0n;
update the conditional that decides which branch to use (the if guarding the
walletClient.writeContract call for withdrawStake) to include "||
feeData.maxFeePerGas === BigInt(0)" (same pattern used in withdrawDeposit,
deposit, deployVp) so that zero maxFeePerGas falls back to legacy; apply the
identical fix to the addStake branch as well so both addStake and withdrawStake
use the same guarded condition involving feeData.maxFeePerGas === BigInt(0) and
skipType2Txns.
- Around line 1272-1276: Update withdrawDeposit to accept an isEpv06: boolean
parameter and pick the ABI and function name based on it: use EtherspotAbiV06
and function name 'withdrawFunds' when isEpv06 is true, otherwise use
EtherspotAbiV07 and 'withdrawTo' for V07+; then call encodeFunctionData with
parseAbi([...]) using the chosen ABI and functionName (refer to withdrawDeposit,
EtherspotAbiV06, EtherspotAbiV07, withdrawFunds, withdrawTo, and
encodeFunctionData) so V06 chains call the correct function signature.

In `@backend/src/routes/admin-routes.ts`:
- Around line 612-713: Extract the duplicated credential and network resolution
logic from withdrawStake (and the similar blocks in addStake,
deployVerifyingPaymaster, deposit routes) into a shared helper like
resolveCredentialsAndNetwork that accepts (apiKey, chainId, epVersion,
apiKeyEntity, unsafeMode, client, prefixSecretId, server.config,
supportedEntrypoints) and returns { privateKey, bundlerApiKey,
supportedNetworks, networkConfig, bundlerUrl }; inside the helper perform the
SecretsManager GetSecretValueCommand flow and unsafeMode decode using
decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET), set bundlerApiKey
from secrets or apiKeyEntity.bundlerApiKey, call getNetworkConfig(chainId,
supportedNetworks, supportedEPs) to build networkConfig and compute bundlerUrl
(append ?api-key= when bundler includes etherspot.io), throw or return errors
consistent with existing ErrorMessage values, then replace the duplicated blocks
in withdrawStake (and the other endpoints) with a call to this helper and use
its returned values (privateKey, bundlerUrl, etc.) when calling
paymaster.withdrawStake.

In `@backend/src/routes/deposit-route.ts`:
- Around line 157-166: The validation block around withdrawAmount (used later
with parseEther) currently allows negative numbers; update the check in the same
block that returns ReturnCode.FAILURE / ErrorMessage.INVALID_DATA to also reject
non-positive values by parsing withdrawAmount to a numeric value (e.g.,
Number(withdrawAmount) or parseFloat) and ensuring it is > 0 and finite before
proceeding; ensure this check occurs before any call to parseEther so parseEther
never receives a negative value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c14e9d2c-8f00-4f8d-8cd4-d3640045b447

📥 Commits

Reviewing files that changed from the base of the PR and between 41f4d17 and 2550e03.

📒 Files selected for processing (4)
  • backend/src/constants/ErrorMessage.ts
  • backend/src/paymaster/index.ts
  • backend/src/routes/admin-routes.ts
  • backend/src/routes/deposit-route.ts

Comment on lines +1272 to +1276
const encodedData = encodeFunctionData({
abi: parseAbi(['function withdrawTo(address withdrawAddress, uint256 amount)']),
functionName: 'withdrawTo',
args: [withdrawAddress as Address, amountInWei]
});
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for withdrawTo in the ABI files to verify the function signature
echo "=== Checking EtherspotAbiV06 for withdrawTo ==="
rg -n "withdrawTo" backend/src/abi/EtherspotAbi.ts

echo "=== Checking EtherspotAbiV07 for withdrawTo ==="
rg -n "withdrawTo" backend/src/abi/EtherspotVerifyingSignerAbi.ts

echo "=== Checking VerifyingPaymaster ABIs ==="
rg -n "withdrawTo" backend/src/abi/VerifyingPaymasterAbi.ts
rg -n "withdrawTo" backend/src/abi/VerifyingPaymasterAbiV2.ts
rg -n "withdrawTo" backend/src/abi/VerifyingPaymasterAbiV3.ts

Repository: etherspot/arka

Length of output: 308


🏁 Script executed:

# Get the withdrawTo function definitions from the ABI files
echo "=== EtherspotVerifyingSignerAbi.ts (V07) around line 451 ==="
sed -n '445,460p' backend/src/abi/EtherspotVerifyingSignerAbi.ts

echo "=== VerifyingPaymasterAbi.ts around line 410 ==="
sed -n '405,420p' backend/src/abi/VerifyingPaymasterAbi.ts

echo "=== VerifyingPaymasterAbiV2.ts around line 307 ==="
sed -n '302,318p' backend/src/abi/VerifyingPaymasterAbiV2.ts

echo "=== VerifyingPaymasterAbiV3.ts around line 307 ==="
sed -n '302,318p' backend/src/abi/VerifyingPaymasterAbiV3.ts

echo "=== Check if EtherspotAbi.ts has any withdrawal-related functions ==="
rg -n "withdraw" backend/src/abi/EtherspotAbi.ts | head -20

Repository: etherspot/arka

Length of output: 34263


🏁 Script executed:

# Check the context around withdrawDeposit method to see if it has version checks
echo "=== Lines around withdrawDeposit method (1258-1320) ==="
sed -n '1250,1320p' backend/src/paymaster/index.ts

echo "=== Check how deposit() method handles versions ==="
sed -n '1180,1220p' backend/src/paymaster/index.ts

Repository: etherspot/arka

Length of output: 5463


Add version parameter and use version-specific ABIs for withdrawDeposit method.

The method uses a hardcoded inline ABI assuming withdrawTo(address, uint256) exists on all paymaster contract versions, but EtherspotAbiV06 does not have this function. The deposit() method correctly handles this with an isEpv06 parameter and conditional ABI selection. Update withdrawDeposit() to:

  1. Add isEpv06: boolean parameter to the method signature
  2. Use conditional ABI selection: isEpv06 ? EtherspotAbiV06 : EtherspotAbiV07
  3. Use version-specific function names: 'withdrawFunds' for V06, 'withdrawTo' for V07+

Without this change, calls to withdrawDeposit() will fail on V06 chains.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/paymaster/index.ts` around lines 1272 - 1276, Update
withdrawDeposit to accept an isEpv06: boolean parameter and pick the ABI and
function name based on it: use EtherspotAbiV06 and function name 'withdrawFunds'
when isEpv06 is true, otherwise use EtherspotAbiV07 and 'withdrawTo' for V07+;
then call encodeFunctionData with parseAbi([...]) using the chosen ABI and
functionName (refer to withdrawDeposit, EtherspotAbiV06, EtherspotAbiV07,
withdrawFunds, withdrawTo, and encodeFunctionData) so V06 chains call the
correct function signature.

Comment on lines +1478 to +1498
let tx;
if (!feeData.maxFeePerGas || this.skipType2Txns.includes(chainId.toString())) {
tx = await walletClient.writeContract({
address: paymasterAddress as Address,
abi: verifyingPaymasterAbi,
functionName: 'withdrawStake',
args: [withdrawAddress],
type: "legacy",
gasPrice: feeData.gasPrice ?? undefined,
});
} else {
tx = await walletClient.writeContract({
address: paymasterAddress as Address,
abi: verifyingPaymasterAbi,
functionName: 'withdrawStake',
args: [withdrawAddress],
maxFeePerGas: feeData.maxFeePerGas ?? undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined,
type: "eip1559"
});
}
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 | 🟠 Major

Inconsistent gas fee check may cause EIP-1559 transactions with zero maxFeePerGas.

The condition at line 1479 is missing || feeData.maxFeePerGas === BigInt(0) which exists in other methods like withdrawDeposit (line 1293), deposit (line 1227), and deployVp (line 1360). If getGasFee returns null and publicClient.getGasPrice() returns 0n, the code will attempt to send an EIP-1559 transaction with maxFeePerGas: 0n, which will likely fail.

The same issue exists in addStake at line 1420.

🐛 Proposed fix
-      if (!feeData.maxFeePerGas || this.skipType2Txns.includes(chainId.toString())) {
+      if (!feeData.maxFeePerGas || this.skipType2Txns.includes(chainId.toString()) || feeData.maxFeePerGas === BigInt(0)) {

Apply the same fix to addStake at line 1420.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let tx;
if (!feeData.maxFeePerGas || this.skipType2Txns.includes(chainId.toString())) {
tx = await walletClient.writeContract({
address: paymasterAddress as Address,
abi: verifyingPaymasterAbi,
functionName: 'withdrawStake',
args: [withdrawAddress],
type: "legacy",
gasPrice: feeData.gasPrice ?? undefined,
});
} else {
tx = await walletClient.writeContract({
address: paymasterAddress as Address,
abi: verifyingPaymasterAbi,
functionName: 'withdrawStake',
args: [withdrawAddress],
maxFeePerGas: feeData.maxFeePerGas ?? undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined,
type: "eip1559"
});
}
let tx;
if (!feeData.maxFeePerGas || this.skipType2Txns.includes(chainId.toString()) || feeData.maxFeePerGas === BigInt(0)) {
tx = await walletClient.writeContract({
address: paymasterAddress as Address,
abi: verifyingPaymasterAbi,
functionName: 'withdrawStake',
args: [withdrawAddress],
type: "legacy",
gasPrice: feeData.gasPrice ?? undefined,
});
} else {
tx = await walletClient.writeContract({
address: paymasterAddress as Address,
abi: verifyingPaymasterAbi,
functionName: 'withdrawStake',
args: [withdrawAddress],
maxFeePerGas: feeData.maxFeePerGas ?? undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined,
type: "eip1559"
});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/paymaster/index.ts` around lines 1478 - 1498, The gas-fee check
for deciding legacy vs EIP-1559 is missing the zero-check and can produce
EIP-1559 txns with maxFeePerGas === 0n; update the conditional that decides
which branch to use (the if guarding the walletClient.writeContract call for
withdrawStake) to include "|| feeData.maxFeePerGas === BigInt(0)" (same pattern
used in withdrawDeposit, deposit, deployVp) so that zero maxFeePerGas falls back
to legacy; apply the identical fix to the addStake branch as well so both
addStake and withdrawStake use the same guarded condition involving
feeData.maxFeePerGas === BigInt(0) and skipType2Txns.

Comment on lines +612 to +713
server.post('/withdrawStake', async (request, reply) => {
try {
if (!request.body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS });

const body: any = request.body;
const query: any = request.query;
const chainId = query['chainId'];
const apiKey = query['apiKey'];
const epVersion = body.params?.[0];
const withdrawAddress = body.params?.[1];

if (!chainId || isNaN(chainId) || !apiKey) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
}

if(!withdrawAddress || !isAddress(withdrawAddress)) {
return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.INVALID_WITHDRAW_ADDRESS});
}

if (!epVersion || (epVersion !== EPVersions.EPV_06 && epVersion !== EPVersions.EPV_07 && epVersion !== EPVersions.EPV_08)) {
return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.INVALID_EP_VERSION});
}

const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(apiKey);
if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY });

let verifyingPaymasters, supportedEPs;

if(epVersion === EPVersions.EPV_06) {
verifyingPaymasters = apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : {};
supportedEPs = SUPPORTED_ENTRYPOINTS.EPV_06;
} else if (epVersion === EPVersions.EPV_07) {
verifyingPaymasters = apiKeyEntity.verifyingPaymastersV2 ? JSON.parse(apiKeyEntity.verifyingPaymastersV2) : {};
supportedEPs = SUPPORTED_ENTRYPOINTS.EPV_07;
} else {
verifyingPaymasters = apiKeyEntity.verifyingPaymastersV3 ? JSON.parse(apiKeyEntity.verifyingPaymastersV3) : {};
supportedEPs = SUPPORTED_ENTRYPOINTS.EPV_08;
}

if (!verifyingPaymasters[chainId]) {
return reply.code(ReturnCode.FAILURE).send(
{error: `${ErrorMessage.VP_NOT_DEPLOYED}`}
);
}

let privateKey;
let bundlerApiKey = apiKey;
let supportedNetworks;

if (!unsafeMode) {
const AWSresponse = await client.send(
new GetSecretValueCommand({
SecretId: prefixSecretId + apiKey,
})
);
const secrets = JSON.parse(AWSresponse.SecretString ?? '{}');
if (!secrets['PRIVATE_KEY']) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY });
}
if (secrets['BUNDLER_API_KEY']) {
bundlerApiKey = secrets['BUNDLER_API_KEY'];
}
privateKey = secrets['PRIVATE_KEY'];
supportedNetworks = secrets['SUPPORTED_NETWORKS'];
} else {
privateKey = decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET);
supportedNetworks = apiKeyEntity.supportedNetworks;
if (apiKeyEntity.bundlerApiKey) {
bundlerApiKey = apiKeyEntity.bundlerApiKey;
}
}

if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK });
}
const networkConfig = getNetworkConfig(
chainId,
supportedNetworks ?? '',
supportedEPs
);
if (!networkConfig) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK });
}
let bundlerUrl = networkConfig.bundler;
if (networkConfig.bundler.includes('etherspot.io')) {
bundlerUrl = `${networkConfig.bundler}?api-key=${bundlerApiKey}`;
}

const tx = await paymaster.withdrawStake(
privateKey,
bundlerUrl,
withdrawAddress,
verifyingPaymasters[chainId],
chainId,
server.log
);
return reply.code(ReturnCode.SUCCESS).send(tx);
} catch (error: any) {
request.log.error(error);
return reply.code(ReturnCode.FAILURE).send({ error: error.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 | 🔵 Trivial

Consider extracting duplicated credential resolution logic.

The private key derivation (lines 657-682) and network configuration resolution (lines 684-698) patterns are duplicated across /withdrawStake, /addStake, /deployVerifyingPaymaster, and deposit routes. Consider extracting this into a shared helper function to improve maintainability.

The endpoint implementation is otherwise correct and follows established patterns.

♻️ Suggested helper extraction
// In a shared utils file
async function resolveCredentialsAndNetwork(
  apiKey: string,
  chainId: number,
  epVersion: EPVersions,
  apiKeyEntity: APIKey,
  unsafeMode: boolean,
  client: SecretsManagerClient,
  prefixSecretId: string,
  serverConfig: any,
  supportedEntrypoints: any
) {
  let privateKey: string;
  let bundlerApiKey = apiKey;
  let supportedNetworks = apiKeyEntity.supportedNetworks;

  if (!unsafeMode) {
    const AWSresponse = await client.send(
      new GetSecretValueCommand({ SecretId: prefixSecretId + apiKey })
    );
    const secrets = JSON.parse(AWSresponse.SecretString ?? '{}');
    if (!secrets['PRIVATE_KEY']) throw new Error('Invalid API key');
    if (secrets['BUNDLER_API_KEY']) bundlerApiKey = secrets['BUNDLER_API_KEY'];
    privateKey = secrets['PRIVATE_KEY'];
    supportedNetworks = secrets['SUPPORTED_NETWORKS'];
  } else {
    privateKey = decode(apiKeyEntity.privateKey, serverConfig.HMAC_SECRET);
    if (apiKeyEntity.bundlerApiKey) bundlerApiKey = apiKeyEntity.bundlerApiKey;
  }

  // ... resolve networkConfig based on epVersion
  return { privateKey, bundlerApiKey, networkConfig, bundlerUrl };
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/routes/admin-routes.ts` around lines 612 - 713, Extract the
duplicated credential and network resolution logic from withdrawStake (and the
similar blocks in addStake, deployVerifyingPaymaster, deposit routes) into a
shared helper like resolveCredentialsAndNetwork that accepts (apiKey, chainId,
epVersion, apiKeyEntity, unsafeMode, client, prefixSecretId, server.config,
supportedEntrypoints) and returns { privateKey, bundlerApiKey,
supportedNetworks, networkConfig, bundlerUrl }; inside the helper perform the
SecretsManager GetSecretValueCommand flow and unsafeMode decode using
decode(apiKeyEntity.privateKey, server.config.HMAC_SECRET), set bundlerApiKey
from secrets or apiKeyEntity.bundlerApiKey, call getNetworkConfig(chainId,
supportedNetworks, supportedEPs) to build networkConfig and compute bundlerUrl
(append ?api-key= when bundler includes etherspot.io), throw or return errors
consistent with existing ErrorMessage values, then replace the duplicated blocks
in withdrawStake (and the other endpoints) with a call to this helper and use
its returned values (privateKey, bundlerUrl, etc.) when calling
paymaster.withdrawStake.

Comment on lines +157 to +166
if (
withdrawAmount === undefined ||
withdrawAmount === null ||
withdrawAmount === '' ||
isNaN(Number(withdrawAmount)) ||
!chainId ||
isNaN(chainId)
) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
}
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 | 🟡 Minor

Negative amount validation is missing.

The validation checks if withdrawAmount is a valid number but doesn't reject negative values. A negative withdrawal amount would be parsed by parseEther and could lead to unexpected behavior.

🛡️ Proposed fix
 if (
     withdrawAmount === undefined ||
     withdrawAmount === null ||
     withdrawAmount === '' ||
     isNaN(Number(withdrawAmount)) ||
+    Number(withdrawAmount) <= 0 ||
     !chainId ||
     isNaN(chainId)
 ) {
     return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (
withdrawAmount === undefined ||
withdrawAmount === null ||
withdrawAmount === '' ||
isNaN(Number(withdrawAmount)) ||
!chainId ||
isNaN(chainId)
) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
}
if (
withdrawAmount === undefined ||
withdrawAmount === null ||
withdrawAmount === '' ||
isNaN(Number(withdrawAmount)) ||
Number(withdrawAmount) <= 0 ||
!chainId ||
isNaN(chainId)
) {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/routes/deposit-route.ts` around lines 157 - 166, The validation
block around withdrawAmount (used later with parseEther) currently allows
negative numbers; update the check in the same block that returns
ReturnCode.FAILURE / ErrorMessage.INVALID_DATA to also reject non-positive
values by parsing withdrawAmount to a numeric value (e.g.,
Number(withdrawAmount) or parseFloat) and ensuring it is > 0 and finite before
proceeding; ensure this check occurs before any call to parseEther so parseEther
never receives a negative value.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/CHANGELOG.md`:
- Line 3: Update the changelog line to clearly state the endpoint intent:
replace “Added endpoints to withdraw stake and deposit.” with wording that
specifies the implemented route is for withdrawing a deposit (e.g., “Added
endpoint withdrawDeposit to withdraw a deposit; not a deposit endpoint”) and
include the route name withdrawDeposit to avoid confusion with a deposit
operation.
- Around line 2-4: The markdown heading "## [4.2.3] - 2026-04-05" in
CHANGELOG.md needs blank lines before and after to satisfy MD022; edit the file
and insert a single empty line above that heading (so it is separated from
previous content) and another empty line immediately after the heading line
(before the "- Added endpoints..." list) to ensure proper heading spacing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02c66f16-8f5c-49d5-b090-c96804e82f62

📥 Commits

Reviewing files that changed from the base of the PR and between 2550e03 and 0f3d1d0.

📒 Files selected for processing (2)
  • backend/CHANGELOG.md
  • backend/package.json

Comment thread backend/CHANGELOG.md
Comment on lines +2 to +4
## [4.2.3] - 2026-04-05
- Added endpoints to withdraw stake and deposit.

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 | 🟡 Minor

Fix markdown heading spacing to satisfy MD022.

Line 2 needs blank lines above and below the heading; current formatting violates markdownlint and can fail docs quality checks.

Suggested patch
 # Changelog
+
 ## [4.2.3] - 2026-04-05
+
 - Added endpoints to withdraw stake and deposit.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## [4.2.3] - 2026-04-05
- Added endpoints to withdraw stake and deposit.
# Changelog
## [4.2.3] - 2026-04-05
- Added endpoints to withdraw stake and deposit.
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 2-2: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/CHANGELOG.md` around lines 2 - 4, The markdown heading "## [4.2.3] -
2026-04-05" in CHANGELOG.md needs blank lines before and after to satisfy MD022;
edit the file and insert a single empty line above that heading (so it is
separated from previous content) and another empty line immediately after the
heading line (before the "- Added endpoints..." list) to ensure proper heading
spacing.

Comment thread backend/CHANGELOG.md
@@ -1,4 +1,7 @@
# Changelog
## [4.2.3] - 2026-04-05
- Added endpoints to withdraw stake and deposit.
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 | 🟡 Minor

Clarify the release note wording for endpoint intent.

Line 3 says “withdraw stake and deposit,” but the implemented route is withdrawDeposit (withdrawing deposit), not depositing. Please make the wording explicit to avoid API confusion.

Suggested wording
-- Added endpoints to withdraw stake and deposit.
+- Added endpoints to withdraw stake and withdraw deposit.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Added endpoints to withdraw stake and deposit.
- Added endpoints to withdraw stake and withdraw deposit.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/CHANGELOG.md` at line 3, Update the changelog line to clearly state
the endpoint intent: replace “Added endpoints to withdraw stake and deposit.”
with wording that specifies the implemented route is for withdrawing a deposit
(e.g., “Added endpoint withdrawDeposit to withdraw a deposit; not a deposit
endpoint”) and include the route name withdrawDeposit to avoid confusion with a
deposit operation.

@nikhilkumar1612 nikhilkumar1612 merged commit 9580b57 into master Apr 5, 2026
4 checks passed
@nikhilkumar1612 nikhilkumar1612 deleted the ft-withdraw branch April 5, 2026 16:03
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.

2 participants