-
Notifications
You must be signed in to change notification settings - Fork 13
ft: adding endpoints for withdrawing stake and deposit #234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,7 @@ | ||||||||||||||||
| # Changelog | ||||||||||||||||
| ## [4.2.3] - 2026-04-05 | ||||||||||||||||
| - Added endpoints to withdraw stake and deposit. | ||||||||||||||||
|
|
||||||||||||||||
|
Comment on lines
+2
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.22.0)[warning] 2-2: Headings should be surrounded by blank lines (MD022, blanks-around-headings) [warning] 2-2: Headings should be surrounded by blank lines (MD022, blanks-around-headings) 🤖 Prompt for AI Agents |
||||||||||||||||
| ## [4.2.2] - 2025-12-09 | ||||||||||||||||
| - Default all the apiKey which would be saved hereafter and update the supportedNetworks to null to make the system only use config.json as default | ||||||||||||||||
| - skips the getDeposit call from cronJob if the network is testnet | ||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1255,6 +1255,67 @@ export class Paymaster { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async withdrawDeposit( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withdrawAddress: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| amount: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| paymasterAddress: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundlerRpc: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| relayerKey: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chainId: number, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log?: FastifyBaseLogger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const viemChain = getViemChainDef(chainId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const publicClient = createPublicClient({ chain: viemChain, transport: http(bundlerRpc) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const walletClient = createWalletClient({ chain: viemChain, transport: http(bundlerRpc), account: privateKeyToAccount(relayerKey as Hex) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const amountInWei = parseEther(amount.toString()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const encodedData = encodeFunctionData({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abi: parseAbi(['function withdrawTo(address withdrawAddress, uint256 amount)']), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| functionName: 'withdrawTo', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| args: [withdrawAddress as Address, amountInWei] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1272
to
+1276
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 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.tsRepository: 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 -20Repository: 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.tsRepository: etherspot/arka Length of output: 5463 Add version parameter and use version-specific ABIs for The method uses a hardcoded inline ABI assuming
Without this change, calls to 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const etherscanFeeData = await getGasFee(chainId, bundlerRpc, log); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const feeData = { gasPrice: BigInt(0), maxFeePerGas: BigInt(0), maxPriorityFeePerGas: BigInt(0) }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (etherscanFeeData) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = etherscanFeeData; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.gasPrice = response.gasPrice ? response.gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxFeePerGas = response.maxFeePerGas ? response.maxFeePerGas + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxPriorityFeePerGas = response.maxPriorityFeePerGas ? response.maxPriorityFeePerGas + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const gasPrice = await publicClient.getGasPrice(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.gasPrice = gasPrice ? gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxFeePerGas = gasPrice ? gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxPriorityFeePerGas = gasPrice ? gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let tx; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!feeData.maxFeePerGas || this.skipType2Txns.includes(chainId.toString()) || feeData.maxFeePerGas === BigInt(0)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tx = await walletClient.sendTransaction({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| to: paymasterAddress as Address, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: encodedData, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gasPrice: feeData.gasPrice ?? undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'legacy', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as TransactionRequest); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tx = await walletClient.sendTransaction({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| to: paymasterAddress as Address, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: encodedData, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxFeePerGas: feeData.maxFeePerGas ?? undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: 'eip1559', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: `Successfully withdrew deposit with transaction Hash ${tx}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log?.error(`error while withdrawing deposit from paymaster ${error}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(ErrorMessage.FAILED_TO_WITHDRAW_DEPOSIT); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async deployVp( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| privateKey: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundlerRpcUrl: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1387,6 +1448,63 @@ export class Paymaster { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async withdrawStake( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| privateKey: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bundlerRpcUrl: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withdrawAddress: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| paymasterAddress: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chainId: number, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log?: FastifyBaseLogger | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const viemChain = getViemChainDef(chainId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const publicClient = createPublicClient({ chain: viemChain, transport: http(bundlerRpcUrl) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const walletClient = createWalletClient({ chain: viemChain, transport: http(bundlerRpcUrl), account: privateKeyToAccount(privateKey as Hex) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const etherscanFeeData = await getGasFee(chainId, bundlerRpcUrl, log); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const feeData = { gasPrice: BigInt(0), maxFeePerGas: BigInt(0), maxPriorityFeePerGas: BigInt(0) }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (etherscanFeeData) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = etherscanFeeData; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.gasPrice = response.gasPrice ? response.gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxFeePerGas = response.maxFeePerGas ? response.maxFeePerGas + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxPriorityFeePerGas = response.maxPriorityFeePerGas ? response.maxPriorityFeePerGas + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const gasPrice = await publicClient.getGasPrice(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.gasPrice = gasPrice ? gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxFeePerGas = gasPrice ? gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| feeData.maxPriorityFeePerGas = gasPrice ? gasPrice + this.feeMarkUp : BigInt(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1478
to
+1498
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent gas fee check may cause EIP-1559 transactions with zero maxFeePerGas. The condition at line 1479 is missing The same issue exists in 🐛 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 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: `Successfully withdrew stake with transaction Hash ${tx}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log?.error(`error while withdrawing stake from verifying paymaster ${error}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(ErrorMessage.FAILED_TO_WITHDRAW_STAKE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async getPriceFromCoingecko(chainId: number, tokenAddress: string, ETHUSDPrice: any, ETHUSDPriceDecimal: any, log?: FastifyBaseLogger): Promise<any> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cacheKey = `${chainId}-${tokenAddress}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const cache = this.coingeckoPrice.get(cacheKey); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,7 +7,8 @@ import { | |
| http, | ||
| getAddress, | ||
| parseEther, | ||
| getContract | ||
| getContract, | ||
| isAddress | ||
| } from "viem"; | ||
| import { privateKeyToAccount } from "viem/accounts"; | ||
| import { ethers } from "ethers"; | ||
|
|
@@ -607,6 +608,109 @@ const adminRoutes: FastifyPluginAsync = async (server) => { | |
| return reply.code(ReturnCode.FAILURE).send({ error: error.message ?? ErrorMessage.FAILED_TO_PROCESS }); | ||
| } | ||
| }); | ||
|
|
||
| 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 }); | ||
| } | ||
| }); | ||
|
Comment on lines
+612
to
+713
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 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 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 |
||
| }; | ||
|
|
||
| export default adminRoutes; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
📝 Committable suggestion
🤖 Prompt for AI Agents