Repository: etherspot/arka
Pull request #198: PRO - 3097 - ARKA EPV08 Support
Author: login: vignesha22
Created: 2025-04-02T03:16:08Z | Merged: 2025-04-17T12:15:48Z

<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in details -->
- Added EPV08 support

## Types of changes
What types of changes does your code introduce?
<!-- _Put an `x` in the boxes that apply_ -->

- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] 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)
<!--- Additional comments, don't hesitate :) -->
-


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

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

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/backend/.env.example b/backend/.env.example
index 5d6ff47..0f1573e 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -11,6 +11,7 @@ FEE_MARKUP=10
 HMAC_SECRET=""
 EPV_06=0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789,0x48e60BBb664aEfAc9f14aDB42e5FB5b4a119EB66
 EPV_07=0x0000000071727De22E5E9d8BAf0edAc6f37da032
+EPV_08=0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108
 
 # postgres database connection
 DATABASE_URL="postgresql://arkauser:paymaster@localhost:5432/arkadev"
diff --git a/backend/CHANGELOG.md b/backend/CHANGELOG.md
index 9a079c8..21461f8 100644
--- a/backend/CHANGELOG.md
+++ b/backend/CHANGELOG.md
@@ -1,4 +1,8 @@
 # Changelog
+## [4.0.0] - 2025-04-01
+### New
+- Added EPV08 Support
+
 ## [3.1.7] - 2025-04-16
 ### New
 - Added support for multiTokenPaymaster deployed for EntryPoint v07
diff --git a/backend/config.json.default b/backend/config.json.default
index 621d4b5..95a90e5 100644
--- a/backend/config.json.default
+++ b/backend/config.json.default
@@ -468,5 +468,15 @@
         "thresholdValue": "0.016",
         "MultiTokenPaymasterOracleUsed": "chainlink",
         "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
+    },
+    {
+        "chainId": 11155111,
+        "bundler": "https://testnet-rpc.etherspot.io/v3/11155111",
+        "contracts": {
+            "etherspotPaymasterAddress": "0xEd5371296A2030F53298a18a3Eb828cFc3dca73f"
+        },
+        "thresholdValue": "0.016",
+        "MultiTokenPaymasterOracleUsed": "chainlink",
+        "entryPoint": "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
     }
 ]
diff --git a/backend/migrations/2025032800001-update-apiKey-table.cjs b/backend/migrations/2025032800001-update-apiKey-table.cjs
new file mode 100644
index 0000000..f6a78e2
--- /dev/null
+++ b/backend/migrations/2025032800001-update-apiKey-table.cjs
@@ -0,0 +1,27 @@
+require('dotenv').config();
+const { DataTypes, TEXT } = require('sequelize');
+
+async function up({ context: queryInterface }) {
+    await queryInterface.addColumn(
+        {schema: process.env.DATABASE_SCHEMA_NAME, tableName: 'api_keys'},
+        'VERIFYING_PAYMASTERS_V3',
+        {
+            type: DataTypes.TEXT,
+            allowNull: true
+        }
+    );
+}
+
+async function down({ context: queryInterface }) {
+    await queryInterface.removeColumn(
+        {schema: process.env.DATABASE_SCHEMA_NAME, tableName: 'api_keys'},
+        'VERIFYING_PAYMASTERS_V3',
+        {
+            type: DataTypes.TEXT,
+            allowNull: true
+        }
+    );
+}
+
+/** @type {import('sequelize-cli').Migration} */
+module.exports = {up, down};
diff --git a/backend/migrations/2025032800002-update-arka-whitelist-table.cjs b/backend/migrations/2025032800002-update-arka-whitelist-table.cjs
new file mode 100644
index 0000000..3ce4ddf
--- /dev/null
+++ b/backend/migrations/2025032800002-update-arka-whitelist-table.cjs
@@ -0,0 +1,27 @@
+require('dotenv').config();
+const { DataTypes } = require('sequelize');
+
+async function up({ context: queryInterface }) {
+    await queryInterface.addColumn(
+        {schema: process.env.DATABASE_SCHEMA_NAME, tableName: 'arka_whitelist'},
+        'EP_VERSION',
+        {
+            type: DataTypes.STRING,
+            allowNull: true
+        }
+    );
+}
+
+async function down({ context: queryInterface }) {
+    await queryInterface.removeColumn(
+        {schema: process.env.DATABASE_SCHEMA_NAME, tableName: 'arka_whitelist'},
+        'EP_VERSION',
+        {
+            type: DataTypes.STRING,
+            allowNull: true
+        }
+    );
+}
+
+/** @type {import('sequelize-cli').Migration} */
+module.exports = {up, down};
diff --git a/backend/package.json b/backend/package.json
index db4f21d..606bad9 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
 {
   "name": "arka",
-  "version": "3.1.7",
+  "version": "4.0.0",
   "description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software",
   "type": "module",
   "directories": {
diff --git a/backend/src/abi/VerifyingPaymasterAbiV3.ts b/backend/src/abi/VerifyingPaymasterAbiV3.ts
new file mode 100644
index 0000000..5baf8e3
--- /dev/null
+++ b/backend/src/abi/VerifyingPaymasterAbiV3.ts
@@ -0,0 +1,315 @@
+export const abi = [
+	{
+		inputs: [
+			{
+				internalType: 'contract IEntryPoint',
+				name: '_entryPoint',
+				type: 'address',
+			},
+			{
+				internalType: 'address',
+				name: '_verifyingSigner',
+				type: 'address',
+			},
+		],
+		stateMutability: 'nonpayable',
+		type: 'constructor',
+	},
+	{ inputs: [], name: 'ECDSAInvalidSignature', type: 'error' },
+	{
+		inputs: [{ internalType: 'uint256', name: 'length', type: 'uint256' }],
+		name: 'ECDSAInvalidSignatureLength',
+		type: 'error',
+	},
+	{
+		inputs: [{ internalType: 'bytes32', name: 's', type: 'bytes32' }],
+		name: 'ECDSAInvalidSignatureS',
+		type: 'error',
+	},
+	{
+		inputs: [{ internalType: 'address', name: 'owner', type: 'address' }],
+		name: 'OwnableInvalidOwner',
+		type: 'error',
+	},
+	{
+		inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
+		name: 'OwnableUnauthorizedAccount',
+		type: 'error',
+	},
+	{
+		anonymous: false,
+		inputs: [
+			{
+				indexed: true,
+				internalType: 'address',
+				name: 'previousOwner',
+				type: 'address',
+			},
+			{
+				indexed: true,
+				internalType: 'address',
+				name: 'newOwner',
+				type: 'address',
+			},
+		],
+		name: 'OwnershipTransferred',
+		type: 'event',
+	},
+	{
+		anonymous: false,
+		inputs: [
+			{
+				indexed: false,
+				internalType: 'address',
+				name: 'newVerifyingSigner',
+				type: 'address',
+			},
+		],
+		name: 'VerifyingSignerUpdated',
+		type: 'event',
+	},
+	{
+		inputs: [
+			{ internalType: 'uint32', name: 'unstakeDelaySec', type: 'uint32' },
+		],
+		name: 'addStake',
+		outputs: [],
+		stateMutability: 'payable',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'deposit',
+		outputs: [],
+		stateMutability: 'payable',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'entryPoint',
+		outputs: [
+			{ internalType: 'contract IEntryPoint', name: '', type: 'address' },
+		],
+		stateMutability: 'view',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'getDeposit',
+		outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
+		stateMutability: 'view',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{
+				components: [
+					{
+						internalType: 'address',
+						name: 'sender',
+						type: 'address',
+					},
+					{ internalType: 'uint256', name: 'nonce', type: 'uint256' },
+					{ internalType: 'bytes', name: 'initCode', type: 'bytes' },
+					{ internalType: 'bytes', name: 'callData', type: 'bytes' },
+					{
+						internalType: 'bytes32',
+						name: 'accountGasLimits',
+						type: 'bytes32',
+					},
+					{
+						internalType: 'uint256',
+						name: 'preVerificationGas',
+						type: 'uint256',
+					},
+					{
+						internalType: 'bytes32',
+						name: 'gasFees',
+						type: 'bytes32',
+					},
+					{
+						internalType: 'bytes',
+						name: 'paymasterAndData',
+						type: 'bytes',
+					},
+					{ internalType: 'bytes', name: 'signature', type: 'bytes' },
+				],
+				internalType: 'struct PackedUserOperation',
+				name: 'userOp',
+				type: 'tuple',
+			},
+			{ internalType: 'uint48', name: 'validUntil', type: 'uint48' },
+			{ internalType: 'uint48', name: 'validAfter', type: 'uint48' },
+		],
+		name: 'getHash',
+		outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }],
+		stateMutability: 'view',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'owner',
+		outputs: [{ internalType: 'address', name: '', type: 'address' }],
+		stateMutability: 'view',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{ internalType: 'bytes', name: 'paymasterAndData', type: 'bytes' },
+		],
+		name: 'parsePaymasterAndData',
+		outputs: [
+			{ internalType: 'uint48', name: 'validUntil', type: 'uint48' },
+			{ internalType: 'uint48', name: 'validAfter', type: 'uint48' },
+			{ internalType: 'bytes', name: 'signature', type: 'bytes' },
+		],
+		stateMutability: 'pure',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{
+				internalType: 'enum IPaymaster.PostOpMode',
+				name: 'mode',
+				type: 'uint8',
+			},
+			{ internalType: 'bytes', name: 'context', type: 'bytes' },
+			{ internalType: 'uint256', name: 'actualGasCost', type: 'uint256' },
+			{
+				internalType: 'uint256',
+				name: 'actualUserOpFeePerGas',
+				type: 'uint256',
+			},
+		],
+		name: 'postOp',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'renounceOwnership',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{ internalType: 'address', name: 'newOwner', type: 'address' },
+		],
+		name: 'transferOwnership',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'unlockStake',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{
+				internalType: 'address',
+				name: '_newVerifyingSigner',
+				type: 'address',
+			},
+		],
+		name: 'updateVerifyingSigner',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{
+				components: [
+					{
+						internalType: 'address',
+						name: 'sender',
+						type: 'address',
+					},
+					{ internalType: 'uint256', name: 'nonce', type: 'uint256' },
+					{ internalType: 'bytes', name: 'initCode', type: 'bytes' },
+					{ internalType: 'bytes', name: 'callData', type: 'bytes' },
+					{
+						internalType: 'bytes32',
+						name: 'accountGasLimits',
+						type: 'bytes32',
+					},
+					{
+						internalType: 'uint256',
+						name: 'preVerificationGas',
+						type: 'uint256',
+					},
+					{
+						internalType: 'bytes32',
+						name: 'gasFees',
+						type: 'bytes32',
+					},
+					{
+						internalType: 'bytes',
+						name: 'paymasterAndData',
+						type: 'bytes',
+					},
+					{ internalType: 'bytes', name: 'signature', type: 'bytes' },
+				],
+				internalType: 'struct PackedUserOperation',
+				name: 'userOp',
+				type: 'tuple',
+			},
+			{ internalType: 'bytes32', name: 'userOpHash', type: 'bytes32' },
+			{ internalType: 'uint256', name: 'maxCost', type: 'uint256' },
+		],
+		name: 'validatePaymasterUserOp',
+		outputs: [
+			{ internalType: 'bytes', name: 'context', type: 'bytes' },
+			{
+				internalType: 'uint256',
+				name: 'validationData',
+				type: 'uint256',
+			},
+		],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [],
+		name: 'verifyingSigner',
+		outputs: [{ internalType: 'address', name: '', type: 'address' }],
+		stateMutability: 'view',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{
+				internalType: 'address payable',
+				name: 'withdrawAddress',
+				type: 'address',
+			},
+		],
+		name: 'withdrawStake',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+	{
+		inputs: [
+			{
+				internalType: 'address payable',
+				name: 'withdrawAddress',
+				type: 'address',
+			},
+			{ internalType: 'uint256', name: 'amount', type: 'uint256' },
+		],
+		name: 'withdrawTo',
+		outputs: [],
+		stateMutability: 'nonpayable',
+		type: 'function',
+	},
+];
+
+export const byteCode =
+	'0x60a060405234801561001057600080fd5b506040516114fc3803806114fc83398101604081905261002f916101d2565b81338061005757604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b61006081610096565b5061006a816100b2565b6001600160a01b03908116608052600280546001600160a01b0319169290911691909117905550610235565b600180546001600160a01b03191690556100af8161016d565b50565b6040516301ffc9a760e01b8152631313998b60e31b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa1580156100fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610121919061020c565b6100af5760405162461bcd60e51b815260206004820152601e60248201527f49456e747279506f696e7420696e74657266616365206d69736d617463680000604482015260640161004e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146100af57600080fd5b600080604083850312156101e557600080fd5b82516101f0816101bd565b6020840151909250610201816101bd565b809150509250929050565b60006020828403121561021e57600080fd5b8151801515811461022e57600080fd5b9392505050565b60805161127b610281600039600081816102a60152818161037a01528181610411015281816106be01528181610758015281816107c801528181610855015261094b015261127b6000f3fe6080604052600436106101095760003560e01c806393b9412111610095578063c23a5cea11610064578063c23a5cea146102dd578063c399ec88146102fd578063d0e30db014610312578063e30c39781461031a578063f2fde38b1461033857600080fd5b806393b941211461024457806394d4ad6014610264578063b0d691fe14610294578063bb9fe6bf146102c857600080fd5b80635829c5f5116100dc5780635829c5f5146101ae578063715018a6146101dc57806379ba5097146101f15780637c627b21146102065780638da5cb5b1461022657600080fd5b80630396cb601461010e578063205c28781461012357806323d9ac9b1461014357806352b7512c14610180575b600080fd5b61012161011c366004610e20565b610358565b005b34801561012f57600080fd5b5061012161013e366004610e62565b6103e3565b34801561014f57600080fd5b50600254610163906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018c57600080fd5b506101a061019b366004610ea7565b610455565b604051610177929190610ef5565b3480156101ba57600080fd5b506101ce6101c9366004610f65565b610479565b604051908152602001610177565b3480156101e857600080fd5b50610121610597565b3480156101fd57600080fd5b506101216105ab565b34801561021257600080fd5b5061012161022136600461100c565b6105f4565b34801561023257600080fd5b506000546001600160a01b0316610163565b34801561025057600080fd5b5061012161025f366004611077565b610610565b34801561027057600080fd5b5061028461027f366004611094565b61066c565b60405161017794939291906110d6565b3480156102a057600080fd5b506101637f000000000000000000000000000000000000000000000000000000000000000081565b3480156102d457600080fd5b506101216106b4565b3480156102e957600080fd5b506101216102f8366004611077565b610731565b34801561030957600080fd5b506101ce6107b0565b610121610840565b34801561032657600080fd5b506001546001600160a01b0316610163565b34801561034457600080fd5b50610121610353366004611077565b6108a2565b610360610913565b604051621cb65b60e51b815263ffffffff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630396cb609034906024016000604051808303818588803b1580156103c757600080fd5b505af11580156103db573d6000803e3d6000fd5b505050505050565b6103eb610913565b60405163040b850f60e31b81526001600160a01b038381166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063205c287890604401600060405180830381600087803b1580156103c757600080fd5b60606000610461610940565b61046c8585856109b0565b915091505b935093915050565b6000806104896020860186611077565b905080602086013561049e6040880188611123565b6040516104ac92919061116a565b6040519081900390206104c26060890189611123565b6040516104d092919061116a565b60405190819003902060808901356104eb60e08b018b611123565b6104fa9160349160149161117a565b610503916111a4565b604080516001600160a01b0390971660208801528601949094526060850192909252608084015260a08084019190915260c08084019290925287013560e0830152860135610100820152466101208201523061014082015265ffffffffffff80861661016083015284166101808201526101a001604051602081830303815290604052805190602001209150509392505050565b61059f610913565b6105a96000610b45565b565b60015433906001600160a01b031681146105e85760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b6105f181610b45565b50565b6105fc610940565b6106098585858585610b5e565b5050505050565b610618610913565b600280546001600160a01b0319166001600160a01b0383169081179091556040519081527f51d754ac8f7adf515a023f2c423e01ef97817c1af33cb63b36f1fe12fde2d91a9060200160405180910390a150565b600080368161067e856034818961117a565b81019061068b91906111c2565b9094509250858561069e603460406111f5565b6106a992829061117a565b949793965094505050565b6106bc610913565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561071757600080fd5b505af115801561072b573d6000803e3d6000fd5b50505050565b610739610913565b60405163611d2e7560e11b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063c23a5cea90602401600060405180830381600087803b15801561079c57600080fd5b505af1158015610609573d6000803e3d6000fd5b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610817573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061083b9190611216565b905090565b60405163b760faf960e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b760faf99034906024016000604051808303818588803b15801561079c57600080fd5b6108aa610913565b600180546001600160a01b0383166001600160a01b031990911681179091556108db6000546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000546001600160a01b031633146105a95760405163118cdaa760e01b81523360048201526024016105df565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146105a95760405162461bcd60e51b815260206004820152601560248201527414d95b99195c881b9bdd08115b9d1c9e541bda5b9d605a1b60448201526064016105df565b60606000808036816109c861027f60e08b018b611123565b9296509094509250905060408114806109e15750604181145b610a55576040805162461bcd60e51b81526020600482015260248101919091527f566572696679696e675061796d61737465723a20696e76616c6964207369676e60448201527f6174757265206c656e67746820696e207061796d6173746572416e644461746160648201526084016105df565b6000610a98610a658b8787610479565b7f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c91909152603c902090565b9050610ada8184848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610b9692505050565b6002546001600160a01b03908116911614610b1a57610afb60018686610bc2565b6040518060200160405280600081525090965096505050505050610471565b610b2660008686610bc2565b6040805160208101909152600081529b909a5098505050505050505050565b600180546001600160a01b03191690556105f181610bf7565b60405162461bcd60e51b815260206004820152600d60248201526c6d757374206f7665727269646560981b60448201526064016105df565b600080600080610ba68686610c47565b925092509250610bb68282610c94565b50909150505b92915050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b85610bea576000610bed565b60015b1717949350505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008060008351604103610c815760208401516040850151606086015160001a610c7388828585610d51565b955095509550505050610c8d565b50508151600091506002905b9250925092565b6000826003811115610ca857610ca861122f565b03610cb1575050565b6001826003811115610cc557610cc561122f565b03610ce35760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115610cf757610cf761122f565b03610d185760405163fce698f760e01b8152600481018290526024016105df565b6003826003811115610d2c57610d2c61122f565b03610d4d576040516335e2f38360e21b8152600481018290526024016105df565b5050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115610d8c5750600091506003905082610e16565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015610de0573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610e0c57506000925060019150829050610e16565b9250600091508190505b9450945094915050565b600060208284031215610e3257600080fd5b813563ffffffff81168114610e4657600080fd5b9392505050565b6001600160a01b03811681146105f157600080fd5b60008060408385031215610e7557600080fd5b8235610e8081610e4d565b946020939093013593505050565b60006101208284031215610ea157600080fd5b50919050565b600080600060608486031215610ebc57600080fd5b833567ffffffffffffffff811115610ed357600080fd5b610edf86828701610e8e565b9660208601359650604090950135949350505050565b604081526000835180604084015260005b81811015610f235760208187018101516060868401015201610f06565b506000606082850101526060601f19601f8301168401019150508260208301529392505050565b803565ffffffffffff81168114610f6057600080fd5b919050565b600080600060608486031215610f7a57600080fd5b833567ffffffffffffffff811115610f9157600080fd5b610f9d86828701610e8e565b935050610fac60208501610f4a565b9150610fba60408501610f4a565b90509250925092565b60008083601f840112610fd557600080fd5b50813567ffffffffffffffff811115610fed57600080fd5b60208301915083602082850101111561100557600080fd5b9250929050565b60008060008060006080868803121561102457600080fd5b85356003811061103357600080fd5b9450602086013567ffffffffffffffff81111561104f57600080fd5b61105b88828901610fc3565b9699909850959660408101359660609091013595509350505050565b60006020828403121561108957600080fd5b8135610e4681610e4d565b600080602083850312156110a757600080fd5b823567ffffffffffffffff8111156110be57600080fd5b6110ca85828601610fc3565b90969095509350505050565b65ffffffffffff8516815265ffffffffffff8416602082015260606040820152816060820152818360808301376000818301608090810191909152601f909201601f191601019392505050565b6000808335601e1984360301811261113a57600080fd5b83018035915067ffffffffffffffff82111561115557600080fd5b60200191503681900382131561100557600080fd5b8183823760009101908152919050565b6000808585111561118a57600080fd5b8386111561119757600080fd5b5050820193919092039150565b80356020831015610bbc57600019602084900360031b1b1692915050565b600080604083850312156111d557600080fd5b6111de83610f4a565b91506111ec60208401610f4a565b90509250929050565b80820180821115610bbc57634e487b7160e01b600052601160045260246000fd5b60006020828403121561122857600080fd5b5051919050565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220d60cea746b68db14dc7e0183ce468ee6205af6d23c8f1e604dc54efcf009e45264736f6c634300081d00330000000000000000000000004337084d9e255ff0702461cf8895ce9e3b5ff10800000000000000000000000080a1874e1046b1cc5defdf4d3153838b72ff94ac';
diff --git a/backend/src/constants/ErrorMessage.ts b/backend/src/constants/ErrorMessage.ts
index 8ea2a56..081490f 100644
--- a/backend/src/constants/ErrorMessage.ts
+++ b/backend/src/constants/ErrorMessage.ts
@@ -56,7 +56,8 @@ export default {
     INVALID_AMOUNT_TO_STAKE: 'Invalid amount to stake',
     NO_KEY_SET: 'No MTP key set',
     MULTI_NOT_DEPLOYED: 'Token Paymaster not deployed on the current chainID: ',
-    COINGECKO_PRICE_NOT_FETCHED: 'Token price not updated, Pls retry.'
+    COINGECKO_PRICE_NOT_FETCHED: 'Token price not updated, Pls retry.',
+    MTP_EP_SUPPORT: 'Currently only EPV06 & EPV07 entryPoint addresses are supported',
 }
 
 export function generateErrorMessage(template: string, values: { [key: string]: string | number }): string {
diff --git a/backend/src/constants/MultitokenPaymaster.ts b/backend/src/constants/MultitokenPaymaster.ts
index 862d1e8..598f418 100644
--- a/backend/src/constants/MultitokenPaymaster.ts
+++ b/backend/src/constants/MultitokenPaymaster.ts
@@ -189,7 +189,8 @@ export const TokenDecimalsAndSymbol: Record<number, Record<string, TokenInfo>> =
         '0x4e352cF164E64ADCBad318C3a1e222E9EBa4Ce42': { decimals: 18, symbol: 'MCB' },
         '0x51fC0f6660482Ea73330E414eFd7808811a57Fa2': { decimals: 18, symbol: 'PREMIA' },
         '0xD74f5255D557944cf7Dd0E45FF521520002D5748': { decimals: 18, symbol: 'USDs' },
-        '0x9ed7E4B1BFF939ad473dA5E7a218C771D1569456': { decimals: 6, symbol: 'REUNI' }
+        '0x9ed7E4B1BFF939ad473dA5E7a218C771D1569456': { decimals: 6, symbol: 'REUNI' },
+        '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': { decimals: 18, symbol: 'ETH' }
     },
     43114: {
         '0x62edc0692BD897D2295872a9FFCac5425011c661': { decimals: 18, symbol: 'GMX' },
diff --git a/backend/src/models/api-key.ts b/backend/src/models/api-key.ts
index a5805c4..aa36062 100644
--- a/backend/src/models/api-key.ts
+++ b/backend/src/models/api-key.ts
@@ -9,6 +9,7 @@ export class APIKey extends Model {
   public erc20PaymastersV2?: string | null;
   public verifyingPaymasters?: string | null;
   public verifyingPaymastersV2?: string | null;
+  public verifyingPaymastersV3?: string | null;
   public multiTokenPaymasters?: string | null;
   public multiTokenPaymastersV2?: string | null;
   public multiTokenOracles?: string | null;
@@ -67,6 +68,11 @@ export function initializeAPIKeyModel(sequelize: Sequelize, schema: string) {
       allowNull: true,
       field: 'VERIFYING_PAYMASTERS_V2'
     },
+    verifyingPaymastersV3: {
+      type: DataTypes.TEXT,
+      allowNull: true,
+      field: 'VERIFYING_PAYMASTERS_V3'
+    },
     bundlerApiKey: {
       type: DataTypes.TEXT,
       allowNull: true,
diff --git a/backend/src/models/whitelist.ts b/backend/src/models/whitelist.ts
index b26b097..c3954ee 100644
--- a/backend/src/models/whitelist.ts
+++ b/backend/src/models/whitelist.ts
@@ -5,6 +5,7 @@ export class ArkaWhitelist extends Model {
     public apiKey!: string;
     public addresses!: string[];
     public policyId?: number;
+    public epVersion?: string | null = null;
     public readonly createdAt!: Date;
     public readonly updatedAt!: Date;
 }
@@ -34,6 +35,11 @@ const initializeArkaWhitelistModel = (sequelize: Sequelize, schema: string) => {
           allowNull: true,
           field: 'POLICY_ID'
         },
+        epVersion: {
+          type: DataTypes.STRING,
+          allowNull: true,
+          field: 'EP_VERSION',
+        },
         createdAt: {
             type: DataTypes.DATE,
             allowNull: false,
diff --git a/backend/src/paymaster/index.ts b/backend/src/paymaster/index.ts
index 2652beb..72b2e2f 100644
--- a/backend/src/paymaster/index.ts
+++ b/backend/src/paymaster/index.ts
@@ -21,6 +21,8 @@ import { CoingeckoService } from '../services/coingecko.js';
 import { Sequelize } from 'sequelize';
 import { abi as verifyingPaymasterAbi, byteCode as verifyingPaymasterByteCode } from '../abi/VerifyingPaymasterAbi.js';
 import { abi as verifyingPaymasterV2Abi, byteCode as verifyingPaymasterV2ByteCode } from '../abi/VerifyingPaymasterAbiV2.js';
+import { abi as verifyingPaymasterV3Abi, byteCode as verifyingPaymasterV3ByteCode } from '../abi/VerifyingPaymasterAbiV3.js';
+import { EPVersions } from '../types/sponsorship-policy-dto.js';
 import MultiTokenPaymasterAbiV2 from '../abi/MultiTokenPaymasterAbiV2.js';
 
 const ttl = parseInt(process.env.CACHE_TTL || "600000");
@@ -55,6 +57,7 @@ export class Paymaster {
   EP7_TOKEN_VGL: string;
   EP7_TOKEN_PGL: string;
   EP7_PVGL: BigNumber;
+  EP8_PVGL: BigNumber;
   MTP_PVGL: string;
   MTP_PPGL: string;
   priceAndMetadata: Map<string, TokenPriceAndMetadataCache> = new Map();
@@ -64,7 +67,7 @@ export class Paymaster {
   sequelize: Sequelize;
 
   constructor(feeMarkUp: string, multiTokenMarkUp: string, ep7TokenVGL: string, ep7TokenPGL: string, sequelize: Sequelize, 
-    mtpVglMarkup: string, ep7Pvgl: string, mtpPvgl: string, mtpPpgl: string) {
+    mtpVglMarkup: string, ep7Pvgl: string, mtpPvgl: string, mtpPpgl: string, ep8Pvgl: string) {
     this.feeMarkUp = ethers.utils.parseUnits(feeMarkUp, 'gwei');
     if (isNaN(Number(multiTokenMarkUp))) this.multiTokenMarkUp = 1150000 // 15% more of the actual cost. Can be anything between 1e6 to 2e6
     else this.multiTokenMarkUp = Number(multiTokenMarkUp);
@@ -73,6 +76,7 @@ export class Paymaster {
     this.sequelize = sequelize;
     this.MTP_VGL_MARKUP = mtpVglMarkup;
     this.EP7_PVGL = BigNumber.from(ep7Pvgl);
+    this.EP8_PVGL = BigNumber.from(ep8Pvgl);
     this.MTP_PVGL = mtpPvgl;
     this.MTP_PPGL = mtpPpgl;
   }
@@ -184,6 +188,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, verifyingPaymasterV3Abi, provider);
+      if (!userOp.signature) userOp.signature = '0x';
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
+      if (!userOp.initCode) userOp.initCode = "0x";
+      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
+      if (estimate) {
+        userOp.paymaster = paymasterAddress;
+        userOp.paymasterVerificationGasLimit = this.EP8_PVGL;
+        userOp.paymasterPostOpGasLimit = paymasterPostOpGasLimit;
+        const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+        const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+        const packedUserOp = {
+          sender: userOp.sender,
+          nonce: userOp.nonce,
+          initCode: userOp.initCode,
+          callData: userOp.callData,
+          accountGasLimits: accountGasLimits,
+          preVerificationGas: userOp.preVerificationGas,
+          gasFees: gasFees,
+          paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP8_PVGL, paymasterPostOpGasLimit),
+          signature: userOp.signature
+        }
+        userOp.paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
+        const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]);
+        userOp.verificationGasLimit = response.verificationGasLimit;
+        userOp.callGasLimit = response.callGasLimit;
+        userOp.preVerificationGas = response.preVerificationGas;
+      }
+      const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+      const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+      const packedUserOp = {
+        sender: userOp.sender,
+        nonce: userOp.nonce,
+        initCode: userOp.initCode,
+        callData: userOp.callData,
+        accountGasLimits: accountGasLimits,
+        preVerificationGas: userOp.preVerificationGas,
+        gasFees: gasFees,
+        paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP8_PVGL, paymasterPostOpGasLimit),
+        signature: userOp.signature
+      }
+
+      const paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
+      let returnValue;
+      if (estimate) {
+        returnValue = {
+          paymaster: paymasterAddress,
+          paymasterData: paymasterData,
+          preVerificationGas: BigNumber.from(packedUserOp.preVerificationGas).toHexString(),
+          verificationGasLimit: BigNumber.from(userOp.verificationGasLimit).toHexString(),
+          callGasLimit: BigNumber.from(userOp.callGasLimit).toHexString(),
+          paymasterVerificationGasLimit: this.EP8_PVGL.toHexString(),
+          paymasterPostOpGasLimit
+        }
+      } else {
+        returnValue = {
+          paymaster: paymasterAddress,
+          paymasterData: paymasterData,
+        }
+      }
+
+      return returnValue;
+    } catch (err: any) {
+      if (err.message.includes("Quota exceeded"))
+        throw new Error('Failed to process request to bundler since request Quota exceeded for the current apiKey')
+      if (log) log.error(err, 'signV08');
+      throw new Error(`Failed to process request to bundler. Please contact support team RawErrorMsg: ${err.message}`)
+    }
+  }
+
   async getPaymasterAndData(userOp: any, validUntil: string, validAfter: string, paymasterContract: Contract, signer: Wallet) {
     // actual signing...
     const hash = await paymasterContract.getHash(
@@ -812,6 +890,7 @@ export class Paymaster {
         const ETHprice = await ecContract.cachedPrice();
         ethPrice = ETHprice
       }
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
       if (!userOp.signature) userOp.signature = '0x';
       userOp.paymaster = paymasterAddress;
       userOp.paymasterVerificationGasLimit = BigNumber.from(this.MTP_PVGL).toHexString(); // Paymaster specific gas limit
@@ -1180,7 +1259,7 @@ export class Paymaster {
     privateKey: string,
     bundlerRpcUrl: string,
     epAddr: string,
-    isEp06: boolean,
+    epVersion: EPVersions,
     chainId: number,
     log?: FastifyBaseLogger
   ) {
@@ -1189,10 +1268,12 @@ export class Paymaster {
       const signer = new Wallet(privateKey, provider);
 
       let contract;
-      if (isEp06) {
+      if (epVersion === EPVersions.EPV_06) {
         contract = new ethers.ContractFactory(verifyingPaymasterAbi, verifyingPaymasterByteCode, signer);
-      } else {
+      } else if (epVersion === EPVersions.EPV_07) {
         contract = new ethers.ContractFactory(verifyingPaymasterV2Abi, verifyingPaymasterV2ByteCode, signer);
+      } else {
+        contract = new ethers.ContractFactory(verifyingPaymasterV3Abi, verifyingPaymasterV3ByteCode, signer);
       }
 
       const etherscanFeeData = await getGasFee(chainId, bundlerRpcUrl, log);
diff --git a/backend/src/plugins/config.ts b/backend/src/plugins/config.ts
index dc2f7ee..7b280ea 100644
--- a/backend/src/plugins/config.ts
+++ b/backend/src/plugins/config.ts
@@ -25,19 +25,21 @@ const ConfigSchema = Type.Strict(
     DATABASE_SCHEMA_NAME: Type.String() || undefined,
     HMAC_SECRET: Type.String({ minLength: 1 }),
     UNSAFE_MODE: Type.Boolean() || undefined,
-    EP7_TOKEN_VGL: Type.String() || '90000',
-    EP7_TOKEN_PGL: Type.String() || '150000',
-    EPV_06: Type.Array(Type.String()) || ['0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'],
-    EPV_07: Type.Array(Type.String()) || ['0x0000000071727De22E5E9d8BAf0edAc6f37da032'],
+    EP7_TOKEN_VGL: Type.String() || undefined,
+    EP7_TOKEN_PGL: Type.String() || undefined,
+    EPV_06: Type.Array(Type.String()),
+    EPV_07: Type.Array(Type.String()),
+    EPV_08: Type.Array(Type.String()),
     DELETE_KEY_RECOVER_WINDOW: Type.Number(),
     KMS_KEY_ID: Type.String() || undefined,
     USE_KMS: Type.Boolean() || false,
     DEFAULT_BUNDLER_API_KEY: Type.String(),
     MULTI_TOKEN_PAYMASTERS: Type.String(),
     MULTI_TOKEN_ORACLES: Type.String(),
-    MTP_VGL_MARKUP: Type.String() || '30000',
+    MTP_VGL_MARKUP: Type.String() || undefined,
     USE_SKANDHA_FOR_GAS_DATA: Type.Boolean() || true,
     EP7_PVGL: Type.String(),
+    EP8_PVGL: Type.String(),
     MTP_PVGL: Type.String() || undefined,
     MTP_PPGL: Type.String() || undefined,
   })
@@ -73,6 +75,7 @@ const configPlugin: FastifyPluginAsync = async (server) => {
     EP7_TOKEN_PGL: process.env.EP7_TOKEN_PGL,
     EPV_06: process.env.EPV_06?.split(','),
     EPV_07: process.env.EPV_07?.split(','),
+    EPV_08: process.env.EPV_08?.split(','),
     DELETE_KEY_RECOVER_WINDOW: process.env.DELETE_KEY_RECOVER_WINDOW,
     KMS_KEY_ID: process.env.KMS_KEY_ID,
     USE_KMS: process.env.USE_KMS,
@@ -82,6 +85,7 @@ const configPlugin: FastifyPluginAsync = async (server) => {
     MTP_VGL_MARKUP: process.env.MTP_VGL_MARKUP,
     USE_SKANDHA_FOR_GAS_DATA: process.env.USE_SKANDHA_FOR_GAS_DATA,
     EP7_PVGL: process.env.EP7_PVGL ?? '30000',
+    EP8_PVGL: process.env.EP8_PVGL ?? '30000',
     MTP_PVGL: process.env.MTP_PVGL ?? '50000',
     MTP_PPGL: process.env.MTP_PPGL ?? '70000',
   }
@@ -113,6 +117,7 @@ const configPlugin: FastifyPluginAsync = async (server) => {
     EP7_TOKEN_PGL: process.env.EP7_TOKEN_PGL ?? '150000',
     EPV_06: process.env.EPV_06?.split(',') ?? ['0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'],
     EPV_07: process.env.EPV_07?.split(',') ?? ['0x0000000071727De22E5E9d8BAf0edAc6f37da032'],
+    EPV_08: process.env.EPV_08?.split(',') ?? ['0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108'],
     DELETE_KEY_RECOVER_WINDOW: parseInt(process.env.DELETE_KEY_RECOVER_WINDOW || '7'),
     KMS_KEY_ID: process.env.KMS_KEY_ID ?? '',
     USE_KMS: process.env.USE_KMS === 'true',
@@ -122,6 +127,7 @@ const configPlugin: FastifyPluginAsync = async (server) => {
     MTP_VGL_MARKUP: process.env.MTP_VGL_MARKUP ?? '30000',
     USE_SKANDHA_FOR_GAS_DATA: process.env.USE_SKANDHA_FOR_GAS_DATA === 'false' ? false : true,
     EP7_PVGL: process.env.EP7_PVGL ?? '30000',
+    EP8_PVGL: process.env.EP8_PVGL ?? '30000',
     MTP_PVGL: process.env.MTP_PVGL ?? '50000',
     MTP_PPGL: process.env.MTP_PPGL ?? '70000',
   }
diff --git a/backend/src/repository/api-key-repository.ts b/backend/src/repository/api-key-repository.ts
index 804ff7b..ed60b74 100644
--- a/backend/src/repository/api-key-repository.ts
+++ b/backend/src/repository/api-key-repository.ts
@@ -1,6 +1,7 @@
 import { Sequelize } from 'sequelize';
 import { APIKey } from '../models/api-key.js';
 import { ApiKeyDto } from '../types/apikey-dto.js';
+import { EPVersions } from '../types/sponsorship-policy-dto.js';
 
 export class APIKeyRepository {
   private sequelize: Sequelize;
@@ -60,10 +61,15 @@ export class APIKeyRepository {
     return result ? result.get() as APIKey : null;
   }
 
-  async updateVpAddresses(apiKey: string, vpAddresses: string, isEp06 = true) {
-    if (isEp06) {
-      return await this.sequelize.models.APIKey.update({verifyingPaymasters: vpAddresses}, {where: {apiKey}});
+  async updateVpAddresses(apiKey: string, vpAddresses: string, epVersion: EPVersions = EPVersions.EPV_06) {
+    if (epVersion === EPVersions.EPV_06) {
+      return await this.sequelize.models.APIKey.update({ verifyingPaymasters: vpAddresses }, { where: { apiKey } });
+    } else if (epVersion === EPVersions.EPV_07) {
+      return await this.sequelize.models.APIKey.update({ verifyingPaymastersV2: vpAddresses }, { where: { apiKey } });
+    } else if (epVersion === EPVersions.EPV_08) {
+      return await this.sequelize.models.APIKey.update({ verifyingPaymastersV3: vpAddresses }, { where: { apiKey } });
+    } else {
+      throw new Error(`Unsupported EP version: ${epVersion}`);
     }
-    return await this.sequelize.models.APIKey.update({verifyingPaymastersV2: vpAddresses}, {where: {apiKey}});
   }
 }
\ No newline at end of file
diff --git a/backend/src/repository/whitelist-repository.ts b/backend/src/repository/whitelist-repository.ts
index a6b6e0f..d0a283c 100644
--- a/backend/src/repository/whitelist-repository.ts
+++ b/backend/src/repository/whitelist-repository.ts
@@ -1,6 +1,7 @@
 import { Sequelize } from 'sequelize';
 import { WhitelistDto } from '../types/whitelist-dto.js';
 import { ArkaWhitelist } from '../models/whitelist.js';
+import { EPVersions } from '../types/sponsorship-policy-dto.js';
 
 export class WhitelistRepository {
   private sequelize: Sequelize;
@@ -14,7 +15,8 @@ export class WhitelistRepository {
     const result = await this.sequelize.models.ArkaWhitelist.create({
       apiKey: apiKey.apiKey,
       addresses: apiKey.addresses,
-      policyId: apiKey.policyId ?? null
+      policyId: apiKey.policyId ?? null,
+      epVersion: apiKey.epVersion ?? null
     }) as ArkaWhitelist;
 
 
@@ -50,6 +52,19 @@ export class WhitelistRepository {
     return result ? result.get() as ArkaWhitelist : null;
   }
 
+  async findOneByApiKeyEPVersionAndPolicyId(apiKey: string, epVersion: EPVersions, policyId?: number): Promise<ArkaWhitelist | null> {
+    let result;
+    if (!epVersion) {
+      return null;
+    }
+    if (policyId) {
+      result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: policyId } });
+    } else {
+      result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: null } });
+    }
+    return result ? result.get() as ArkaWhitelist : null;
+  }
+
   async updateOneById(record: ArkaWhitelist): Promise<ArkaWhitelist | null> {
     const result = await this.sequelize.models.ArkaWhitelist.update({
       apiKey: record.apiKey,
diff --git a/backend/src/routes/admin-routes.ts b/backend/src/routes/admin-routes.ts
index 1ebafe1..2b39c35 100644
--- a/backend/src/routes/admin-routes.ts
+++ b/backend/src/routes/admin-routes.ts
@@ -19,7 +19,7 @@ import { Paymaster } from "../paymaster/index.js";
 
 const adminRoutes: FastifyPluginAsync = async (server) => {
   const paymaster = new Paymaster(server.config.FEE_MARKUP, server.config.MULTI_TOKEN_MARKUP, server.config.EP7_TOKEN_VGL, server.config.EP7_TOKEN_PGL, server.sequelize, 
-    server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL);
+    server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL, server.config.EP8_PVGL);
 
   const prefixSecretId = 'arka_';
 
@@ -33,7 +33,8 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
 
   const SUPPORTED_ENTRYPOINTS = {
     EPV_06: server.config.EPV_06,
-    EPV_07: server.config.EPV_07
+    EPV_07: server.config.EPV_07,
+    EPV_08: server.config.EPV_08
   }
 
 
@@ -385,21 +386,25 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
         return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
       }
 
-      if (!epVersion || (epVersion !== EPVersions.EPV_06 && epVersion !== EPVersions.EPV_07)) {
+      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 isEp06 = epVersion === EPVersions.EPV_06;
-
       const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(apiKey);
       if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY });
 
       let verifyingPaymasters;
+      let supportedEPs;
 
-      if(isEp06) {
+      if(epVersion === EPVersions.EPV_06) {
         verifyingPaymasters = apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : {};
-      } else {
+        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(
@@ -441,7 +446,7 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
       const networkConfig = getNetworkConfig(
         chainId,
         supportedNetworks ?? '',
-        isEp06 ? SUPPORTED_ENTRYPOINTS.EPV_06 : SUPPORTED_ENTRYPOINTS.EPV_07
+        supportedEPs
       );
       
       if (!networkConfig) {
@@ -456,12 +461,12 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
         privateKey,
         bundlerUrl,
         networkConfig.entryPoint,
-        isEp06,
+        epVersion,
         chainId,
         server.log
       );
       verifyingPaymasters[chainId] = address;
-      await server.apiKeyRepository.updateVpAddresses(apiKey, JSON.stringify(verifyingPaymasters), isEp06);
+      await server.apiKeyRepository.updateVpAddresses(apiKey, JSON.stringify(verifyingPaymasters), epVersion);
 
       return reply.code(ReturnCode.SUCCESS).send({verifyingPaymaster: address, txHash: hash});
     } catch (error: any) {
@@ -489,21 +494,24 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
         return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.INVALID_AMOUNT_TO_STAKE});
       }
 
-      if (!epVersion || (epVersion !== EPVersions.EPV_06 && epVersion !== EPVersions.EPV_07)) {
+      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 isEp06 = epVersion === EPVersions.EPV_06;
-
       const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(apiKey);
       if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY });
 
-      let verifyingPaymasters;
+      let verifyingPaymasters, supportedEPs;
 
-      if(isEp06) {
+      if(epVersion === EPVersions.EPV_06) {
         verifyingPaymasters = apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : {};
-      } else {
+        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]) {
@@ -545,7 +553,7 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
       const networkConfig = getNetworkConfig(
         chainId,
         supportedNetworks ?? '',
-        isEp06 ? SUPPORTED_ENTRYPOINTS.EPV_06 : SUPPORTED_ENTRYPOINTS.EPV_07
+        supportedEPs
       );
       if (!networkConfig) {
         return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK });
diff --git a/backend/src/routes/deposit-route.ts b/backend/src/routes/deposit-route.ts
index a6f1aff..6fd8a96 100644
--- a/backend/src/routes/deposit-route.ts
+++ b/backend/src/routes/deposit-route.ts
@@ -1,6 +1,6 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 import { Type } from "@sinclair/typebox";
-import { FastifyPluginAsync } from "fastify";
+import { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
 import { GetSecretValueCommand, SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
 import { Paymaster } from "../paymaster/index.js";
 import SupportedNetworks from "../../config.json" assert { type: "json" };
@@ -9,14 +9,16 @@ import ReturnCode from "../constants/ReturnCode.js";
 import { decode } from "../utils/crypto.js";
 import { printRequest, getNetworkConfig } from "../utils/common.js";
 import { APIKey } from "../models/api-key.js";
+import { EPVersions } from "../types/sponsorship-policy-dto.js";
 
 const depositRoutes: FastifyPluginAsync = async (server) => {
     const paymaster = new Paymaster(server.config.FEE_MARKUP, server.config.MULTI_TOKEN_MARKUP, server.config.EP7_TOKEN_VGL, server.config.EP7_TOKEN_PGL, server.sequelize, 
-        server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL);
+        server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL, server.config.EP8_PVGL);
 
     const SUPPORTED_ENTRYPOINTS = {
         EPV_06: server.config.EPV_06,
-        EPV_07: server.config.EPV_07
+        EPV_07: server.config.EPV_07,
+        EPV_08: server.config.EPV_08
     }
 
     const prefixSecretId = 'arka_';
@@ -42,145 +44,109 @@ const depositRoutes: FastifyPluginAsync = async (server) => {
         }
     }
 
+    async function deposit(request: FastifyRequest, reply: FastifyReply, epVersion: EPVersions) {
+        try {
+            const body: any = request.body;
+            if (!body) {
+                return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.EMPTY_BODY })
+            }
+            const query: any = request.query;
+            const amount = body.params?.[0];
+            const useVp = query['useVp'] ?? false;
+            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 = '';
+            let bundlerApiKey = api_key;
+            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 (apiKeyEntity.bundlerApiKey) {
+                bundlerApiKey = apiKeyEntity.bundlerApiKey;
+            }
+            if (
+                isNaN(amount) ||
+                !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 });
+            }
+            let networkConfig;
+            let vpAddr;
+            if (EPVersions.EPV_06 == epVersion) {
+                networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_06);
+                vpAddr = apiKeyEntity.verifyingPaymasters ? 
+                            JSON.parse(apiKeyEntity.verifyingPaymasters)[chainId] :
+                            undefined;
+            } else if (EPVersions.EPV_07 == epVersion) {
+                networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_07);
+                vpAddr = apiKeyEntity.verifyingPaymastersV2 ? 
+                            JSON.parse(apiKeyEntity.verifyingPaymastersV2)[chainId] :
+                            undefined;
+            } else if (EPVersions.EPV_08 == epVersion) {
+                networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_08);
+                vpAddr = apiKeyEntity.verifyingPaymastersV3 ? 
+                            JSON.parse(apiKeyEntity.verifyingPaymastersV3)[chainId] :
+                            undefined;
+            }
+            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}`;
+
+            if(!useVp) {
+                return await paymaster.deposit(amount, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, privateKey, chainId, true, server.log);
+            }
+            if(!vpAddr) {
+                return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED})
+            }
+
+            return await paymaster.deposit(amount, vpAddr, bundlerUrl, privateKey, chainId, false, server.log);
+        } 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 })
+        }
+    }
+
     server.post("/deposit",
         ResponseSchema,
         async function (request, reply) {
-            try {
-                printRequest("/deposit", request, server.log);
-                const body: any = request.body;
-                if (!body) {
-                    return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.EMPTY_BODY })
-                }
-                const query: any = request.query;
-                const amount = body.params?.[0];
-                const useVp = query['useVp'] ?? false;
-                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 = '';
-                let bundlerApiKey = api_key;
-                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 (apiKeyEntity.bundlerApiKey) {
-                    bundlerApiKey = apiKeyEntity.bundlerApiKey;
-                }
-                if (
-                    isNaN(amount) ||
-                    !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_06);
-                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}`;
-
-                if(!useVp) {
-                    return await paymaster.deposit(amount, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, privateKey, chainId, true, server.log);
-                }
-                const vpAddr = apiKeyEntity.verifyingPaymasters ? 
-                                JSON.parse(apiKeyEntity.verifyingPaymasters)[chainId] :
-                                undefined;
-                if(!vpAddr) {
-                    return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED})
-                }
-
-                return await paymaster.deposit(amount, vpAddr, bundlerUrl, privateKey, chainId, false, server.log);
-            } 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 })
-            }
+            printRequest("/deposit", request, server.log);
+            return await deposit(request, reply, EPVersions.EPV_06);
         }
     )
 
     server.post("/deposit/v2",
         ResponseSchema,
         async function (request, reply) {
-            try {
-                printRequest("/deposit/v2", request, server.log);
-                const body: any = request.body;
-                const query: any = request.query;
-                if (!body) {
-                    return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.EMPTY_BODY })
-                }
-                const amount = body.params?.[0];
-                const chainId = query['chainId'] ?? body.params?.[1];
-                const api_key = query['apiKey'] ?? body.params?.[2];
-                const useVp = query['useVp'] ?? false;
-                if (!api_key || typeof(api_key) !== "string")
-                    return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY })
-                let privateKey = '';
-                let bundlerApiKey = api_key;
-                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 (apiKeyEntity.bundlerApiKey) {
-                    bundlerApiKey = apiKeyEntity.bundlerApiKey;
-                }
-                if (
-                    isNaN(amount) ||
-                    !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_07);
-                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}`;
+            printRequest("/deposit/v2", request, server.log);
+            return await deposit(request, reply, EPVersions.EPV_07);
+        }
+    )
 
-                if(!useVp) {
-                    return await paymaster.deposit(amount, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, privateKey, chainId, false, server.log);
-                }
-                const vpAddr = apiKeyEntity.verifyingPaymastersV2 ? 
-                                JSON.parse(apiKeyEntity.verifyingPaymastersV2)[chainId] :
-                                undefined;
-                if(!vpAddr) {
-                    return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED})
-                }
-                return await paymaster.deposit(amount, vpAddr, bundlerUrl, privateKey, chainId, false, server.log);
-            } 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 })
-            }
+    server.post("/deposit/v3",
+        ResponseSchema,
+        async function (request, reply) {
+            printRequest("/deposit/v3", request, server.log);
+            return await deposit(request, reply, EPVersions.EPV_08);
         }
     )
 };
diff --git a/backend/src/routes/metadata-routes.ts b/backend/src/routes/metadata-routes.ts
index 1b30bdd..8678dcd 100644
--- a/backend/src/routes/metadata-routes.ts
+++ b/backend/src/routes/metadata-routes.ts
@@ -11,6 +11,8 @@ import { PAYMASTER_ADDRESS } from "../constants/Token.js";
 import { APIKey } from "../models/api-key.js";
 import * as EtherspotAbi from "../abi/EtherspotAbi.js";
 import {abi as verifyingPaymasterAbi} from "../abi/VerifyingPaymasterAbi.js";
+import {abi as verifyingPaymasterV2Abi} from "../abi/VerifyingPaymasterAbiV2.js";
+import {abi as verifyingPaymastersV3Abi} from "../abi/VerifyingPaymasterAbiV3.js";
 
 const metadataRoutes: FastifyPluginAsync = async (server) => {
 
@@ -18,7 +20,8 @@ const metadataRoutes: FastifyPluginAsync = async (server) => {
 
   const SUPPORTED_ENTRYPOINTS = {
     EPV_06: server.config.EPV_06,
-    EPV_07: server.config.EPV_07
+    EPV_07: server.config.EPV_07,
+    EPV_08: server.config.EPV_08
   }
 
   let client: SecretsManagerClient;
@@ -119,6 +122,7 @@ const metadataRoutes: FastifyPluginAsync = async (server) => {
         verifyingPaymaster: { address: verifyingPaymaster, deposit: verifyingPaymasterDeposit },
         verifyingPaymasters: apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : undefined,
         verifyingPaymastersV2: apiKeyEntity.verifyingPaymastersV2 ? JSON.parse(apiKeyEntity.verifyingPaymastersV2) : undefined,
+        verifyingPaymastersV3: apiKeyEntity.verifyingPaymastersV3 ? JSON.parse(apiKeyEntity.verifyingPaymastersV3) : undefined,
       })
     } catch (err: any) {
       request.log.error(err);
@@ -195,7 +199,7 @@ const metadataRoutes: FastifyPluginAsync = async (server) => {
       const verifyingPaymaster = apiKeyEntity.verifyingPaymastersV2 ? JSON.parse(apiKeyEntity.verifyingPaymastersV2)[chainId] : undefined;
       let verifyingPaymasterDeposit;
       if (verifyingPaymaster) {
-        const vpContract = new Contract(verifyingPaymaster, verifyingPaymasterAbi ,provider);
+        const vpContract = new Contract(verifyingPaymaster, verifyingPaymasterV2Abi ,provider);
         verifyingPaymasterDeposit = await vpContract.getDeposit();
       }
       const chainsSupported: { chainId: number, entryPoint: string }[] = [];
@@ -217,6 +221,103 @@ const metadataRoutes: FastifyPluginAsync = async (server) => {
         verifyingPaymaster: { address: verifyingPaymaster, deposit: verifyingPaymasterDeposit },
         verifyingPaymasters: apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : undefined,
         verifyingPaymastersV2: apiKeyEntity.verifyingPaymastersV2 ? JSON.parse(apiKeyEntity.verifyingPaymastersV2) : undefined,
+        verifyingPaymastersV3: apiKeyEntity.verifyingPaymastersV3 ? JSON.parse(apiKeyEntity.verifyingPaymastersV3) : undefined,
+      })
+    } 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 });
+    }
+  })
+
+  server.get('/metadata/v3', async function (request, reply) {
+    try {
+      printRequest('/metadata/v3', request, server.log);
+      const query: any = request.query;
+      const chainId = query['chainId'] ?? 137;
+      const api_key = query['apiKey'];
+      if (!api_key || typeof(api_key) !== "string")
+        return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY })
+      if (!chainId)
+        return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA })
+      let customPaymasters = [];
+      let multiTokenPaymasters = [];
+      let privateKey = '';
+      let sponsorName = '', sponsorImage = '';
+      let bundlerApiKey = api_key;
+      const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(api_key);
+      if (!apiKeyEntity) {
+        server.log.info("Invalid Api Key provided")
+        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']) {
+          server.log.info("Invalid Api Key provided")
+          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 (apiKeyEntity.erc20Paymasters) {
+        const buffer = Buffer.from(apiKeyEntity.erc20Paymasters, 'base64');
+        customPaymasters = JSON.parse(buffer.toString());
+      }
+      if (apiKeyEntity.multiTokenPaymasters) {
+        const buffer = Buffer.from(apiKeyEntity.multiTokenPaymasters, 'base64');
+        multiTokenPaymasters = JSON.parse(buffer.toString());
+      }
+      if (apiKeyEntity.bundlerApiKey) {
+        bundlerApiKey = apiKeyEntity.bundlerApiKey;
+      }
+      sponsorName = apiKeyEntity.sponsorName ? apiKeyEntity.sponsorName : "";
+      sponsorImage = apiKeyEntity.logoUrl ? apiKeyEntity.logoUrl : "";
+      const supportedNetworks = apiKeyEntity.supportedNetworks;
+      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 });
+      let bundlerUrl = networkConfig.bundler;
+      if (bundlerUrl.includes('etherspot.io')) bundlerUrl = `${networkConfig.bundler}?api-key=${bundlerApiKey}`;
+      const provider = new providers.JsonRpcProvider(bundlerUrl);
+      const signer = new Wallet(privateKey, provider)
+      const sponsorWalletBalance = await signer.getBalance();
+      const sponsorAddress = await signer.getAddress();
+
+      const verifyingPaymaster = apiKeyEntity.verifyingPaymastersV3 ? JSON.parse(apiKeyEntity.verifyingPaymastersV3)[chainId] : undefined;
+      let verifyingPaymasterDeposit;
+      if (verifyingPaymaster) {
+        const vpContract = new Contract(verifyingPaymaster, verifyingPaymastersV3Abi ,provider);
+        verifyingPaymasterDeposit = await vpContract.getDeposit();
+      }
+      const chainsSupported: { chainId: number, entryPoint: string }[] = [];
+      SupportedNetworks.map(element => {
+        chainsSupported.push({ chainId: element.chainId, entryPoint: element.entryPoint });
+      })
+      const tokenPaymasterAddresses = {
+        ...PAYMASTER_ADDRESS,
+        ...customPaymasters,
+      }
+      return reply.code(ReturnCode.SUCCESS).send({
+        sponsorAddress: sponsorAddress,
+        sponsorWalletBalance: sponsorWalletBalance,
+        sponsorBalance: verifyingPaymasterDeposit ?? 0,
+        chainsSupported: chainsSupported,
+        tokenPaymasters: tokenPaymasterAddresses,
+        multiTokenPaymasters,
+        sponsorDetails: { name: sponsorName, icon: sponsorImage },
+        verifyingPaymaster: { address: verifyingPaymaster, deposit: verifyingPaymasterDeposit },
+        verifyingPaymasters: apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : undefined,
+        verifyingPaymastersV2: apiKeyEntity.verifyingPaymastersV2 ? JSON.parse(apiKeyEntity.verifyingPaymastersV2) : undefined,
+        verifyingPaymastersV3: apiKeyEntity.verifyingPaymastersV3 ? JSON.parse(apiKeyEntity.verifyingPaymastersV3) : undefined,
       })
     } catch (err: any) {
       request.log.error(err);
diff --git a/backend/src/routes/paymaster-routes.ts b/backend/src/routes/paymaster-routes.ts
index 5b97c76..8e5d989 100644
--- a/backend/src/routes/paymaster-routes.ts
+++ b/backend/src/routes/paymaster-routes.ts
@@ -20,7 +20,8 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
 
   const SUPPORTED_ENTRYPOINTS = {
     EPV_06: server.config.EPV_06,
-    EPV_07: server.config.EPV_07
+    EPV_07: server.config.EPV_07,
+    EPV_08: server.config.EPV_08
   }
 
   const prefixSecretId = 'arka_';
@@ -88,11 +89,11 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
         }
         if (!api_key || typeof (api_key) !== "string")
           return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY })
-        if (!SUPPORTED_ENTRYPOINTS.EPV_06?.includes(entryPoint) && !SUPPORTED_ENTRYPOINTS.EPV_07?.includes(entryPoint))
-          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_ENTRYPOINT })
-
+        
         if (SUPPORTED_ENTRYPOINTS.EPV_06?.includes(entryPoint)) epVersion = EPVersions.EPV_06;
-        else epVersion = EPVersions.EPV_07;
+        else if (SUPPORTED_ENTRYPOINTS.EPV_07?.includes(entryPoint)) epVersion = EPVersions.EPV_07;
+        else if (SUPPORTED_ENTRYPOINTS.EPV_08?.includes(entryPoint)) epVersion = EPVersions.EPV_08;
+        else return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_ENTRYPOINT })
 
         let customPaymasters = [];
         let customPaymastersV2 = [];
@@ -254,15 +255,19 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
                 const contractWhitelistResult = await checkContractWhitelist(userOp.callData, chainId.chainId, signer.address);
                 if (!contractWhitelistResult) throw new Error('Contract Method not whitelisted');
               }
+              const isWhitelisted = await checkWhitelist(api_key, epVersion, userOp.sender, sponsorshipPolicy.id);
+              if (!isWhitelisted) {
+                throw new Error('This sender address has not been whitelisted yet');
+              }
               if (epVersion === EPVersions.EPV_06)
                 result = await paymaster.signV06(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
-              else {
-                const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
-                if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
-                  const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
-                  if (!existingWhitelistRecord?.addresses.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
+              else if (epVersion === EPVersions.EPV_07) {
+                if (!networkConfig.contracts.etherspotPaymasterAddress) {
+                  throw new Error('Please use useVP flag to use your deployed verifying paymaster as global paymaster is not defined');
                 }
                 result = await paymaster.signV07(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
+              } else {
+                result = await paymaster.signV08(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
               }
               break;
             }
@@ -380,10 +385,9 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
                 if (!contractWhitelistResult) throw new Error('Contract Method not whitelisted');
               }
 
-              const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
-              if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
-                const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
-                if (!existingWhitelistRecord?.addresses.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
+              const isWhitelisted = await checkWhitelist(api_key, epVersion, userOp.sender, sponsorshipPolicy.id);
+              if (!isWhitelisted) {
+                throw new Error('This sender address has not been whitelisted yet');
               }
 
               if (epVersion === EPVersions.EPV_06) {
@@ -396,15 +400,24 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
                 }
                 result = await paymaster.signV06(userOp, str, str1, entryPoint, paymasterAddr, bundlerUrl, signer, estimate, server.log);
               }
-              else {
-                if (!apiKeyEntity.verifyingPaymastersV2) {
-                  return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.VP_NOT_DEPLOYED });
+              else if  (epVersion === EPVersions.EPV_07) {
+                if(!apiKeyEntity.verifyingPaymastersV2) {
+                  return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED});
                 }
                 const paymasterAddr = JSON.parse(apiKeyEntity.verifyingPaymastersV2)[chainId.chainId];
                 if (!paymasterAddr) {
                   return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.VP_NOT_DEPLOYED });
                 }
                 result = await paymaster.signV07(userOp, str, str1, entryPoint, paymasterAddr, bundlerUrl, signer, estimate, server.log);
+              } else {
+                if (!apiKeyEntity.verifyingPaymastersV3) {
+                  return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED});
+                }
+                const paymasterAddr = JSON.parse(apiKeyEntity.verifyingPaymastersV3)[chainId.chainId];
+                if (!paymasterAddr) {
+                  return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED});
+                }
+                result = await paymaster.signV08(userOp, str, str1, entryPoint, paymasterAddr, bundlerUrl, signer, estimate, server.log);
               }
               break;
             }
@@ -574,6 +587,23 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
     }
     return returnValue;
   }
+
+  async function checkWhitelist(api_key: string, epVersion: EPVersions, senderAddress: string, policyId: number) {
+    const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
+    if (!globalWhitelistRecord?.addresses.includes(senderAddress)) {
+      const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+      if (!existingWhitelistRecord?.addresses.includes(senderAddress)) {
+        const existingEpWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion, policyId);
+        if (!existingEpWhitelistRecord?.addresses.includes(senderAddress)) {
+          const existingEpWhitelistRecord2 = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion);
+          if (!existingEpWhitelistRecord2?.addresses.includes(senderAddress)) {
+            return false;
+          }
+        }
+      }
+    }
+    return true;
+  }
 };
 
 export default paymasterRoutes;
\ No newline at end of file
diff --git a/backend/src/routes/whitelist-routes.ts b/backend/src/routes/whitelist-routes.ts
index 2894254..11103a3 100644
--- a/backend/src/routes/whitelist-routes.ts
+++ b/backend/src/routes/whitelist-routes.ts
@@ -10,14 +10,16 @@ import { decode } from "../utils/crypto.js";
 import { printRequest, getNetworkConfig } from "../utils/common.js";
 import { APIKey } from "../models/api-key.js";
 import { ContractWhitelistDto } from "../types/contractWhitelist-dto.js";
+import { EPVersions } from "types/sponsorship-policy-dto.js";
 
 const whitelistRoutes: FastifyPluginAsync = async (server) => {
   const paymaster = new Paymaster(server.config.FEE_MARKUP, server.config.MULTI_TOKEN_MARKUP, server.config.EP7_TOKEN_VGL, server.config.EP7_TOKEN_PGL, server.sequelize, 
-    server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL);
+    server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL, server.config.EP8_PVGL);
 
   const SUPPORTED_ENTRYPOINTS = {
     EPV_06: server.config.EPV_06,
-    EPV_07: server.config.EPV_07
+    EPV_07: server.config.EPV_07,
+    EPV_08: server.config.EPV_08
   }
 
   const prefixSecretId = 'arka_';
@@ -46,6 +48,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         } else {
           address = body.params?.[0];
           policyId = body.params?.[1];
+          if (policyId && isNaN(policyId)) {
+            return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+          }
           api_key = query['apiKey'] ?? body.params?.[2];
         }
         if (!api_key || typeof(api_key) !== "string")
@@ -151,6 +156,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         } else {
           address = body.params?.[0];
           policyId = body.params?.[1];
+          if (policyId && isNaN(policyId)) {
+            return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+          }
           chainId = query['chainId'] ?? body.params?.[2];
           api_key = query['apiKey'] ?? body.params?.[3];
         }
@@ -263,6 +271,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         } else {
           accountAddress = body.params?.[0];
           policyId = body.params?.[1];
+          if (policyId && isNaN(policyId)) {
+            return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+          }
           chainId = query['chainId'] ?? body.params?.[2];
           api_key = query['apiKey'] ?? body.params?.[3];
         }
@@ -345,6 +356,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS });
         const address = body.params?.[0];
         const policyId = body.params?.[1];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
         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 })
@@ -380,22 +394,34 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
           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.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
 
-        if (existingWhitelistRecord) {
+        if (existingWhitelistRecord || existingGlobalWhitelistRecord) {
           const toBeAdded: string[] = [];
           address.filter(ele => {
-            if (!existingWhitelistRecord.addresses.includes(ele)) toBeAdded.push(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 });
-          const allAddresses = toBeAdded.concat(existingWhitelistRecord.addresses);
-          existingWhitelistRecord.addresses = allAddresses;
-          await server.whitelistRepository.updateOneById(existingWhitelistRecord);
+          if (existingWhitelistRecord) {
+            const allAddresses = toBeAdded.concat(existingWhitelistRecord.addresses);
+            existingWhitelistRecord.addresses = allAddresses;
+            await server.whitelistRepository.updateOneById(existingWhitelistRecord);
+          } else {
+            const addWhitelistDto = {
+              apiKey: api_key,
+              addresses: toBeAdded,
+              policyId: policyId ?? null,
+              epVersion: EPVersions.EPV_07
+            }
+            await server.whitelistRepository.create(addWhitelistDto);
+          }
         } else {
           const addWhitelistDto = {
             apiKey: api_key,
             addresses: address,
             policyId: policyId ?? null,
+            epVersion: EPVersions.EPV_07
           }
           await server.whitelistRepository.create(addWhitelistDto);
         }
@@ -422,6 +448,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS });
         const address = body.params?.[0];
         const policyId = body.params?.[1];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
         const chainId = query['chainId'] ?? body.params?.[2];
         const api_key = query['apiKey'] ?? body.params?.[3];
         if (!api_key || typeof(api_key) !== "string")
@@ -458,21 +487,38 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         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.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
+        const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        let isGlobal = false;
 
-
-        if (existingWhitelistRecord) {
+        if (existingWhitelistRecord || existingGlobalWhitelistRecord) {
           const toBeRemoved: string[] = [];
           address.filter(ele => {
-            if (existingWhitelistRecord.addresses.includes(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 (existingWhitelistRecord.addresses.length < 1) await server.whitelistRepository.deleteById(existingWhitelistRecord.id);
-          else await server.whitelistRepository.updateOneById(existingWhitelistRecord);
+          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);
         }
@@ -499,6 +545,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.MISSING_PARAMS });
         const accountAddress = body.params?.[0];
         const policyId = body.params?.[1];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
         const chainId = query['chainId'] ?? body.params?.[2];
         const api_key = query['apiKey'] ?? body.params?.[3];
         if (!accountAddress || !ethers.utils.isAddress(accountAddress)) {
@@ -536,12 +585,14 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         }
         const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_07);
         if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK });
-        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        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 })
@@ -562,6 +613,9 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         const body: any = request.body;
         const query: any = request.query;
         const policyId = body?.params?.[0];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
         const chainId = query['chainId'] ?? body?.params?.[1];
         const api_key = query['apiKey'] ?? body?.params?.[2];
         if (!api_key || typeof(api_key) !== "string")
@@ -594,12 +648,16 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         }
         const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_07);
         if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK });
+        // global whitelist
         const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
 
-        if (!existingWhitelistRecord) {
+        // EpVersion specific whitelist
+        const existingEpWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
+
+        if (!existingWhitelistRecord && !existingEpWhitelistRecord) {
           throw new Error(ErrorMessage.NO_WHITELIST_FOUND);
         }
-        const result = { addresses: existingWhitelistRecord.addresses }
+        const result = { addresses: [...(existingWhitelistRecord?.addresses || []), ...(existingEpWhitelistRecord?.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 })
@@ -613,6 +671,326 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
     }
   )
 
+  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];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
+        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 })
+      }
+    }
+  );
+
+  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];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
+        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 })
+      }
+    }
+  );
+
+  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];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
+        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 })
+      }
+    }
+  );
+
+  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];
+        if (policyId && isNaN(policyId)) {
+          return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
+        }
+        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);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_08, policyId);
+        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 })
+      }
+    }  )
+
   server.post("/whitelistContractAddress",
     async function (request, reply) {
       try {
diff --git a/backend/src/server.ts b/backend/src/server.ts
index fd87b58..44fe2bc 100644
--- a/backend/src/server.ts
+++ b/backend/src/server.ts
@@ -62,7 +62,7 @@ const initializeServer = async (): Promise<void> => {
   // Register the sequelizePlugin
   await server.register(sequelizePlugin);
   const paymaster = new Paymaster(server.config.FEE_MARKUP, server.config.MULTI_TOKEN_MARKUP, server.config.EP7_TOKEN_VGL, server.config.EP7_TOKEN_PGL, server.sequelize, 
-    server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL);
+    server.config.MTP_VGL_MARKUP, server.config.EP7_PVGL, server.config.MTP_PVGL, server.config.MTP_PPGL, server.config.EP8_PVGL);
 
   // Synchronize all models
   await server.sequelize.sync();
@@ -263,7 +263,9 @@ const initializeServer = async (): Promise<void> => {
                     ) {
                       const thresholdValue = network.thresholdValue ?? networkConfig.thresholdValue;
                       const bundler = network.bundler ?? networkConfig.bundler;
-                      checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                      if (network.contracts?.etherspotPaymasterAddress) {
+                        checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                      }
                     }
                   }
                 }
@@ -306,7 +308,9 @@ const initializeServer = async (): Promise<void> => {
 
               // checking deposit for epv6 native paymasters from default config.json.
               for (const network of SupportedNetworks) {
-                checkDeposit(network.contracts.etherspotPaymasterAddress, network.bundler, process.env.WEBHOOK_URL, network.thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                if (network.contracts?.etherspotPaymasterAddress) {
+                  checkDeposit(network.contracts.etherspotPaymasterAddress, network.bundler, process.env.WEBHOOK_URL, network.thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                }
               }
 
               // Checking of Deposit on common multi token paymaster if any
diff --git a/backend/src/types/sponsorship-policy-dto.ts b/backend/src/types/sponsorship-policy-dto.ts
index c40f4c9..1529786 100644
--- a/backend/src/types/sponsorship-policy-dto.ts
+++ b/backend/src/types/sponsorship-policy-dto.ts
@@ -35,6 +35,7 @@ export interface SponsorshipPolicyDto {
 export enum EPVersions {
     EPV_06 = 'EPV_06',
     EPV_07 = 'EPV_07',
+    EPV_08 = 'EPV_08',
     // Add more versions here as needed
 }
 
@@ -47,6 +48,8 @@ export function getEPVersion(value: string): EPVersions {
             return EPVersions.EPV_06;
         case 'EPV_07':
             return EPVersions.EPV_07;
+        case 'EPV_08':
+            return EPVersions.EPV_08;
         default:
             throw new Error('Unsupported EP version');
     }
@@ -59,6 +62,8 @@ export function getEPVersionString(value: EPVersions): string {
             return 'EPV_06';
         case EPVersions.EPV_07:
             return 'EPV_07';
+        case EPVersions.EPV_08:
+            return 'EPV_08';
         default:
             throw new Error('Unsupported EP version');
     }
diff --git a/backend/src/types/whitelist-dto.ts b/backend/src/types/whitelist-dto.ts
index e3bbc02..aa9d29f 100644
--- a/backend/src/types/whitelist-dto.ts
+++ b/backend/src/types/whitelist-dto.ts
@@ -2,4 +2,5 @@ export interface WhitelistDto {
   apiKey: string;
   addresses: string[];
   policyId?: number;
+  epVersion?: string;
 }
\ No newline at end of file


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 17**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>backend/src/abi/VerifyingPaymasterAbiV3.ts (1)</summary><blockquote>

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

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 265596f2a565f4aa96ffdbdbb6fa0c9c102e6b6e and 032d8fd78f25a2fdac0922e63ae03a5996c18548.

</details>

<details>
<summary>📒 Files selected for processing (21)</summary>

* `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)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (3)</summary>

<details>
<summary>backend/src/server.ts (1)</summary><blockquote>

<details>
<summary>backend/src/utils/monitorTokenPaymaster.ts (1)</summary>

* `checkDeposit` (6-22)

</details>

</blockquote></details>
<details>
<summary>backend/src/repository/whitelist-repository.ts (1)</summary><blockquote>

<details>
<summary>backend/src/models/whitelist.ts (1)</summary>

* `ArkaWhitelist` (3-11)

</details>

</blockquote></details>
<details>
<summary>backend/src/routes/whitelist-routes.ts (4)</summary><blockquote>

<details>
<summary>backend/src/paymaster/index.ts (1)</summary>

* `Paymaster` (52-1267)

</details>
<details>
<summary>backend/src/utils/common.ts (2)</summary>

* `printRequest` (6-10)
* `getNetworkConfig` (12-23)

</details>
<details>
<summary>backend/src/models/api-key.ts (1)</summary>

* `APIKey` (3-24)

</details>
<details>
<summary>backend/src/utils/crypto.ts (1)</summary>

* `decode` (31-43)

</details>

</blockquote></details>

</details><details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

<details>
<summary>backend/CHANGELOG.md</summary>

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)

</details>

</details>
<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>backend/src/paymaster/index.ts</summary>

[error] 253-253: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>
<details>
<summary>backend/src/routes/admin-routes.ts</summary>

[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)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Cloudflare Pages

</details>

<details>
<summary>🔇 Additional comments (32)</summary><blockquote>

<details>
<summary>backend/src/types/whitelist-dto.ts (1)</summary>

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

</details>
<details>
<summary>backend/package.json (1)</summary>

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

</details>
<details>
<summary>backend/src/routes/metadata-routes.ts (1)</summary>

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

</details>
<details>
<summary>backend/CHANGELOG.md (2)</summary>

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

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

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)

</details>

</details>

---

`2-5`: <details>
<summary>❓ Verification inconclusive</summary>

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

```diff
 # Changelog
+
 ## [3.2.0] - 2025-04-01
+
 ### New
+
 - Added EPV08 Support

 ## [3.1.5] - 2025-03-24
```

---

🏁 Script executed:

```shell
#!/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

---



</details>

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.

```shell
#!/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
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.17.2)</summary>

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)

</details>

</details>

</details>
<details>
<summary>backend/migrations/2025032800001-update-apiKey-table.cjs (1)</summary>

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

</details>
<details>
<summary>backend/src/constants/MultitokenPaymaster.ts (1)</summary>

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

</details>
<details>
<summary>backend/migrations/2025032800002-update-arka-whitelist-table.cjs (1)</summary>

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

</details>
<details>
<summary>backend/src/models/api-key.ts (2)</summary>

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

</details>
<details>
<summary>backend/src/models/whitelist.ts (2)</summary>

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

</details>
<details>
<summary>backend/src/server.ts (1)</summary>

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

</details>
<details>
<summary>backend/src/plugins/config.ts (3)</summary>

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

</details>
<details>
<summary>backend/src/types/sponsorship-policy-dto.ts (3)</summary>

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

</details>
<details>
<summary>backend/config.json.default (1)</summary>

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

</details>
<details>
<summary>backend/src/repository/whitelist-repository.ts (2)</summary>

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

</details>
<details>
<summary>backend/src/routes/deposit-route.ts (2)</summary>

`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?

</details>
<details>
<summary>backend/src/repository/api-key-repository.ts (1)</summary>

`64-71`: <details>
<summary>❓ Verification inconclusive</summary>

**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:

```shell
#!/bin/bash
# Find all places where updateVpAddresses is called
rg "updateVpAddresses\(" --type typescript
```

Length of output: 79

---



</details>

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:

```shell
#!/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.

</details>
<details>
<summary>backend/src/routes/admin-routes.ts (2)</summary>

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

</details>
<details>
<summary>backend/src/paymaster/index.ts (1)</summary>

`24-25`: **Importing new ABI and EPVersions looks good.**

Including the V3 code references and enumeration expands support for EPV_08 effectively.

</details>
<details>
<summary>backend/src/routes/paymaster-routes.ts (2)</summary>

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

</details>
<details>
<summary>backend/src/routes/whitelist-routes.ts (2)</summary>

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

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T03:22:45Z
user: login: nikhilkumar1612
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-07T11:13:50Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T06:46:45Z
user: login: nikhilkumar1612
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T06:56:37Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:15:49Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:16:42Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:17:26Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:17:35Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:18:17Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:19:19Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:19:36Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T08:20:21Z
user: login: ch4r10t33r
body: LGTM
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-16T09:45:58Z
user: login: nikhilkumar1612
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-16T10:23:47Z
user: login: vignesha22
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T09:33:27Z
user: login: nikhilkumar1612
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-17T10:02:23Z

Review comments:
diff_hunk: @@ -18,7 +18,8 @@ const metadataRoutes: FastifyPluginAsync = async (server) => {
 
   const SUPPORTED_ENTRYPOINTS = {
     EPV_06: server.config.EPV_06,
-    EPV_07: server.config.EPV_07
+    EPV_07: server.config.EPV_07,
+    EPV_08: server.config.EPV_08
   }
path: backend/src/routes/metadata-routes.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider implementing V3 metadata endpoint for EPV_08**

While you've added EPV_08 to the SUPPORTED_ENTRYPOINTS object, there's no dedicated endpoint for EPV_08 similar to how you have `/metadata/v2` for EPV_07. For consistency, consider adding a `/metadata/v3` endpoint to support EPV_08 specific configurations.

```diff
 const SUPPORTED_ENTRYPOINTS = {
   EPV_06: server.config.EPV_06,
   EPV_07: server.config.EPV_07,
   EPV_08: server.config.EPV_08
 }

+// Later in the file
+server.get('/metadata/v3', async function (request, reply) {
+  try {
+    printRequest('/metadata/v3', request, server.log);
+    // Similar implementation to metadata/v2 but using SUPPORTED_ENTRYPOINTS.EPV_08
+    // ...
+  } catch (err: any) {
+    // Error handling
+  }
+})
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
  const SUPPORTED_ENTRYPOINTS = {
    EPV_06: server.config.EPV_06,
    EPV_07: server.config.EPV_07,
    EPV_08: server.config.EPV_08
  }

  // Later in the file
  server.get('/metadata/v3', async function (request, reply) {
    try {
      printRequest('/metadata/v3', request, server.log);
      // Similar implementation to metadata/v2 but using SUPPORTED_ENTRYPOINTS.EPV_08
      // ...
    } catch (err: any) {
      // Error handling
    }
  })
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -262,7 +262,9 @@ const initializeServer = async (): Promise<void> => {
                     ) {
                       const thresholdValue = network.thresholdValue ?? networkConfig.thresholdValue;
                       const bundler = network.bundler ?? networkConfig.bundler;
-                      checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                      if (network.contract.etherspotPaymasterAddress) {
+                        checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                      }
path: backend/src/server.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Added null check for etherspotPaymasterAddress to prevent errors.**

This change improves the code's robustness by ensuring that `checkDeposit` is only called when `network.contract.etherspotPaymasterAddress` exists, preventing potential null reference errors.


However, there appears to be a typo in the property access - it should be `network.contracts` (plural) instead of `network.contract` (singular) to match line 310 and the contract usage throughout the rest of the file.

```diff
-if (network.contract.etherspotPaymasterAddress) {
-  checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+if (network.contracts?.etherspotPaymasterAddress) {
+  checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
                      if (network.contracts?.etherspotPaymasterAddress) {
                        checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
                      }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -50,6 +52,16 @@ export class WhitelistRepository {
     return result ? result.get() as ArkaWhitelist : null;
   }
 
+  async findOneByApiKeyEPVersionAndPolicyId(apiKey: string, epVersion: EPVersions, policyId?: number): Promise<ArkaWhitelist | null> {
+    let result;
+    if (policyId) {
+      result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: policyId } });
+    } else {
+      result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: null } });
+    }
+    return result ? result.get() as ArkaWhitelist : null;
+  }
path: backend/src/repository/whitelist-repository.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider adding null check for epVersion parameter**

The new method follows the same pattern as the existing findOneByApiKeyAndPolicyId method with an additional filter for epVersion. However, consider adding a null check for the epVersion parameter to improve robustness, similar to how you handle policyId.


```diff
async findOneByApiKeyEPVersionAndPolicyId(apiKey: string, epVersion: EPVersions, policyId?: number): Promise<ArkaWhitelist | null> {
  let result;
+  if (!epVersion) {
+    return null;
+  }
  if (policyId) {
    result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: policyId } });
  } else {
    result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: null } });
  }
  return result ? result.get() as ArkaWhitelist : null;
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
  async findOneByApiKeyEPVersionAndPolicyId(apiKey: string, epVersion: EPVersions, policyId?: number): Promise<ArkaWhitelist | null> {
    let result;
    if (!epVersion) {
      return null;
    }
    if (policyId) {
      result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: policyId } });
    } else {
      result = await this.sequelize.models.ArkaWhitelist.findOne({ where: { apiKey: apiKey, epVersion: epVersion, policyId: null } });
    }
    return result ? result.get() as ArkaWhitelist : null;
  }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -182,6 +183,72 @@ const depositRoutes: FastifyPluginAsync = async (server) => {
             }
         }
     )
+
+    server.post("/deposit/v3",
+        ResponseSchema,
+        async function (request, reply) {
+            try {
+                printRequest("/deposit/v3", request, server.log);
+                const body: any = request.body;
+                const query: any = request.query;
+                if (!body) {
+                    return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.EMPTY_BODY })
+                }
+                const amount = 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 = '';
+                let bundlerApiKey = api_key;
+                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 (apiKeyEntity.bundlerApiKey) {
+                    bundlerApiKey = apiKeyEntity.bundlerApiKey;
+                }
+                if (
+                    isNaN(amount) ||
+                    !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 });
+                let bundlerUrl = networkConfig.bundler;
+                if (networkConfig.bundler.includes('etherspot.io')) bundlerUrl = `${networkConfig.bundler}?api-key=${bundlerApiKey}`;
+
+                const vpAddr = apiKeyEntity.verifyingPaymastersV3 ? 
+                                JSON.parse(apiKeyEntity.verifyingPaymastersV3)[chainId] :
+                                undefined;
+                if(!vpAddr) {
+                    return reply.code(ReturnCode.FAILURE).send({error: ErrorMessage.VP_NOT_DEPLOYED})
+                }
+                return await paymaster.deposit(amount, vpAddr, bundlerUrl, privateKey, chainId, false, server.log);
+            } 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 })
+            }
+        }
+    )
path: backend/src/routes/deposit-route.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Refactor endpoint implementation to reduce code duplication**

The new /deposit/v3 endpoint duplicates a significant amount of code from the existing /deposit and /deposit/v2 endpoints. This violates the DRY principle and could make future maintenance difficult.

Also, unlike the /deposit/v2 endpoint, the v3 endpoint doesn't include a `useVp` parameter - it always tries to use the verifying paymaster. This is a behavior change that should be intentional and documented.


Consider refactoring to extract the common logic into a shared function:

```diff
+ async function handleDepositRequest(request, reply, epVersion: string, server, client, unsafeMode, prefixSecretId, paymaster) {
+   try {
+     const body: any = request.body;
+     const query: any = request.query;
+     if (!body) {
+       return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.EMPTY_BODY })
+     }
+     const amount = body.params?.[0];
+     const chainId = query['chainId'] ?? body.params?.[1];
+     const api_key = query['apiKey'] ?? body.params?.[2];
+     const useVp = epVersion === 'EPV_08' ? true : (query['useVp'] ?? false);
+     
+     // ... rest of the common logic ...
+     
+     const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS[epVersion]);
+     
+     // ... more common logic ...
+     
+     // Version-specific handling
+     if (epVersion === 'EPV_06') {
+       // Logic for EPV_06
+     } else if (epVersion === 'EPV_07') {
+       // Logic for EPV_07
+     } else {
+       // Logic for EPV_08
+     }
+   } catch (err: any) {
+     // Error handling
+   }
+ }
+
+ server.post("/deposit", ResponseSchema, async function(request, reply) {
+   return handleDepositRequest(request, reply, 'EPV_06', server, client, unsafeMode, prefixSecretId, paymaster);
+ });
+
+ server.post("/deposit/v2", ResponseSchema, async function(request, reply) {
+   return handleDepositRequest(request, reply, 'EPV_07', server, client, unsafeMode, prefixSecretId, paymaster);
+ });
+
+ server.post("/deposit/v3", ResponseSchema, async function(request, reply) {
+   return handleDepositRequest(request, reply, 'EPV_08', server, client, unsafeMode, prefixSecretId, paymaster);
+ });
```


> Committable suggestion skipped: line range outside the PR's diff.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -1,6 +1,7 @@
 import { Sequelize } from 'sequelize';
 import { APIKey } from '../models/api-key.js';
 import { ApiKeyDto } from '../types/apikey-dto.js';
+import { EPVersions } from 'types/sponsorship-policy-dto.js';
path: backend/src/repository/api-key-repository.ts
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Correct the import path**

The import path should be relative or absolute, not starting directly from a directory name.


```diff
-import { EPVersions } from 'types/sponsorship-policy-dto.js';
+import { EPVersions } from '../types/sponsorship-policy-dto.js';
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
import { EPVersions } from '../types/sponsorship-policy-dto.js';
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -60,10 +61,13 @@ export class APIKeyRepository {
     return result ? result.get() as APIKey : null;
   }
 
-  async updateVpAddresses(apiKey: string, vpAddresses: string, isEp06 = true) {
-    if (isEp06) {
+  async updateVpAddresses(apiKey: string, vpAddresses: string, epVersion: EPVersions) {
+    if (epVersion === EPVersions.EPV_06) {
       return await this.sequelize.models.APIKey.update({verifyingPaymasters: vpAddresses}, {where: {apiKey}});
+    } else if (epVersion === EPVersions.EPV_07) {
+      return await this.sequelize.models.APIKey.update({verifyingPaymastersV2: vpAddresses}, {where: {apiKey}});
+    } else {
+      return await this.sequelize.models.APIKey.update({verifyingPaymastersV3: vpAddresses}, {where: {apiKey}});
     }
path: backend/src/repository/api-key-repository.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Update method signature to ensure backward compatibility**

The method previously defaulted the `isEp06` parameter to `true`, but the new version requires an explicit `epVersion` with no default. This could potentially break existing callers of this method if they were relying on the default value.

Also, the conditional structure with `if ... else if ... else` assumes that anything that's not EPV_06 or EPV_07 should be treated as EPV_08. This might lead to unexpected behavior if more versions are added in the future.


```diff
-async updateVpAddresses(apiKey: string, vpAddresses: string, epVersion: EPVersions) {
+async updateVpAddresses(apiKey: string, vpAddresses: string, epVersion: EPVersions = EPVersions.EPV_06) {
  if (epVersion === EPVersions.EPV_06) {
    return await this.sequelize.models.APIKey.update({verifyingPaymasters: vpAddresses}, {where: {apiKey}});
  } else if (epVersion === EPVersions.EPV_07) {
    return await this.sequelize.models.APIKey.update({verifyingPaymastersV2: vpAddresses}, {where: {apiKey}});
-  } else {
+  } else if (epVersion === EPVersions.EPV_08) {
    return await this.sequelize.models.APIKey.update({verifyingPaymastersV3: vpAddresses}, {where: {apiKey}});
+  } else {
+    throw new Error(`Unsupported EP version: ${epVersion}`);
  }
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
  async updateVpAddresses(apiKey: string, vpAddresses: string, epVersion: EPVersions = EPVersions.EPV_06) {
    if (epVersion === EPVersions.EPV_06) {
      return await this.sequelize.models.APIKey.update({ verifyingPaymasters: vpAddresses }, { where: { apiKey } });
    } else if (epVersion === EPVersions.EPV_07) {
      return await this.sequelize.models.APIKey.update({ verifyingPaymastersV2: vpAddresses }, { where: { apiKey } });
    } else if (epVersion === EPVersions.EPV_08) {
      return await this.sequelize.models.APIKey.update({ verifyingPaymastersV3: vpAddresses }, { where: { apiKey } });
    } else {
      throw new Error(`Unsupported EP version: ${epVersion}`);
    }
  }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -384,21 +385,24 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
         return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA });
       }
 
-      if (!epVersion || (epVersion !== EPVersions.EPV_06 && epVersion !== EPVersions.EPV_07)) {
+      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 isEp06 = epVersion === EPVersions.EPV_06;
-
       const apiKeyEntity: APIKey | null = await server.apiKeyRepository.findOneByApiKey(apiKey);
       if (!apiKeyEntity) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY });
 
-      let verifyingPaymasters;
+      let verifyingPaymasters, supportedEPs;
 
-      if(isEp06) {
+      if(epVersion === EPVersions.EPV_06) {
         verifyingPaymasters = apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : {};
-      } else {
+        supportedEPs = SUPPORTED_ENTRYPOINTS.EPV_06;
path: backend/src/routes/admin-routes.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Consider declaring variables separately.**

A static analysis tool indicates a style preference to avoid multiple variable declarations in a single statement. You could do:

```diff
- let verifyingPaymasters, supportedEPs;
+ let verifyingPaymasters;
+ let supportedEPs;
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
      let verifyingPaymasters;
      let supportedEPs;

      if(epVersion === EPVersions.EPV_06) {
        verifyingPaymasters = apiKeyEntity.verifyingPaymasters ? JSON.parse(apiKeyEntity.verifyingPaymasters) : {};
        supportedEPs = SUPPORTED_ENTRYPOINTS.EPV_06;
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 395-395: Declare variables separately

Unsafe fix: Break out into multiple declarations


(lint/style/useSingleVarDeclarator)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:43Z
author_association: NONE
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
+      if (!userOp.signature) userOp.signature = '0x';
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
+      if (!userOp.initCode) userOp.initCode = "0x";
+      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
+      if (estimate) {
+        userOp.paymaster = paymasterAddress;
+        userOp.paymasterVerificationGasLimit = this.EP7_PVGL;
+        userOp.paymasterPostOpGasLimit = paymasterPostOpGasLimit;
+        const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+        const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+        const packedUserOp = {
+          sender: userOp.sender,
+          nonce: userOp.nonce,
+          initCode: userOp.initCode,
+          callData: userOp.callData,
+          accountGasLimits: accountGasLimits,
+          preVerificationGas: userOp.preVerificationGas,
+          gasFees: gasFees,
+          paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
+          signature: userOp.signature
+        }
+        userOp.paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
+        const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]);
+        userOp.verificationGasLimit = response.verificationGasLimit;
+        userOp.callGasLimit = response.callGasLimit;
+        userOp.preVerificationGas = response.preVerificationGas;
+      }
+      const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+      const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+      const packedUserOp = {
+        sender: userOp.sender,
+        nonce: userOp.nonce,
+        initCode: userOp.initCode,
+        callData: userOp.callData,
+        accountGasLimits: accountGasLimits,
+        preVerificationGas: userOp.preVerificationGas,
+        gasFees: gasFees,
+        paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
+        signature: userOp.signature
+      }
+
+      const paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
+      let returnValue;
+      if (estimate) {
+        returnValue = {
+          paymaster: paymasterAddress,
+          paymasterData: paymasterData,
+          preVerificationGas: BigNumber.from(packedUserOp.preVerificationGas).toHexString(),
+          verificationGasLimit: BigNumber.from(userOp.verificationGasLimit).toHexString(),
+          callGasLimit: BigNumber.from(userOp.callGasLimit).toHexString(),
+          paymasterVerificationGasLimit: this.EP7_PVGL.toHexString(),
+          paymasterPostOpGasLimit
+        }
+      } else {
+        returnValue = {
+          paymaster: paymasterAddress,
+          paymasterData: paymasterData,
+        }
+      }
+
+      return returnValue;
+    } catch (err: any) {
+      if (err.message.includes("Quota exceeded"))
+        throw new Error('Failed to process request to bundler since request Quota exceeded for the current apiKey')
+      if (log) log.error(err, 'signV08');
+      throw new Error('Failed to process request to bundler. Please contact support team RawErrorMsg:' + err.message)
+    }
+  }
+
path: backend/src/paymaster/index.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Use template literals for clearer error reporting.**

In the catch block, string concatenation is used for `'RawErrorMsg:' + err.message`. Refactor to a template literal for stylistic consistency:

```diff
- throw new Error('Failed to process request to bundler. Please contact support team RawErrorMsg:' + err.message)
+ throw new Error(`Failed to process request to bundler. Please contact support team RawErrorMsg: ${err.message}`)
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
    try {
      const provider = new providers.JsonRpcProvider(bundlerRpc);
      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
      if (!userOp.signature) userOp.signature = '0x';
      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
      if (!userOp.initCode) userOp.initCode = "0x";
      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
      if (estimate) {
        userOp.paymaster = paymasterAddress;
        userOp.paymasterVerificationGasLimit = this.EP7_PVGL;
        userOp.paymasterPostOpGasLimit = paymasterPostOpGasLimit;
        const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
        const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
        const packedUserOp = {
          sender: userOp.sender,
          nonce: userOp.nonce,
          initCode: userOp.initCode,
          callData: userOp.callData,
          accountGasLimits: accountGasLimits,
          preVerificationGas: userOp.preVerificationGas,
          gasFees: gasFees,
          paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
          signature: userOp.signature
        }
        userOp.paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
        const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]);
        userOp.verificationGasLimit = response.verificationGasLimit;
        userOp.callGasLimit = response.callGasLimit;
        userOp.preVerificationGas = response.preVerificationGas;
      }
      const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
      const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
      const packedUserOp = {
        sender: userOp.sender,
        nonce: userOp.nonce,
        initCode: userOp.initCode,
        callData: userOp.callData,
        accountGasLimits: accountGasLimits,
        preVerificationGas: userOp.preVerificationGas,
        gasFees: gasFees,
        paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
        signature: userOp.signature
      }

      const paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
      let returnValue;
      if (estimate) {
        returnValue = {
          paymaster: paymasterAddress,
          paymasterData: paymasterData,
          preVerificationGas: BigNumber.from(packedUserOp.preVerificationGas).toHexString(),
          verificationGasLimit: BigNumber.from(userOp.verificationGasLimit).toHexString(),
          callGasLimit: BigNumber.from(userOp.callGasLimit).toHexString(),
          paymasterVerificationGasLimit: this.EP7_PVGL.toHexString(),
          paymasterPostOpGasLimit
        }
      } else {
        returnValue = {
          paymaster: paymasterAddress,
          paymasterData: paymasterData,
        }
      }

      return returnValue;
    } catch (err: any) {
      if (err.message.includes("Quota exceeded"))
        throw new Error('Failed to process request to bundler since request Quota exceeded for the current apiKey')
      if (log) log.error(err, 'signV08');
      throw new Error(`Failed to process request to bundler. Please contact support team RawErrorMsg: ${err.message}`)
    }
  }
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 253-253: Template literals are preferred over string concatenation.

Unsafe fix: Use a template literal.


(lint/style/useTemplate)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -254,13 +255,26 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
               }
               if (epVersion === EPVersions.EPV_06)
                 result = await paymaster.signV06(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
-              else {
+              else if (epVersion === EPVersions.EPV_07) {
+                if (!networkConfig.contracts.etherspotPaymasterAddress) {
+                  throw new Error('Please use useVP flag to use your deployed verifying paymaster as global paymaster is not defined');
+                }
                 const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
                 if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
                   const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
                   if (!existingWhitelistRecord?.addresses.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
                 }
                 result = await paymaster.signV07(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
+              } else {
+                const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
+                if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
+                  const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
+                  const existingEpWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion, sponsorshipPolicy.id);
+                  const existingEpWhitelistRecord2 = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion);
+                  const combinedList = [...(existingWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord2?.addresses ?? [])];
+                  if (!combinedList.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
+                }
+                result = await paymaster.signV08(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
               }
path: backend/src/routes/paymaster-routes.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Duplicated whitelist checks.**

The new EPV_08 block mirrors EPV_07 logic with slight variations. Consider extracting shared logic into a helper function to minimize repetition.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -369,7 +383,10 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
               const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
               if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
                 const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
-                if (!existingWhitelistRecord?.addresses.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
+                const existingEpWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion, sponsorshipPolicy.id);
+                const existingEpWhitelistRecord2 = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion);
+                const combinedList = [...(existingWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord2?.addresses ?? [])];
+                if (!combinedList.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
               }
path: backend/src/routes/paymaster-routes.ts
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Combine multiple whitelist checks.**

Merging addresses from different whitelist records is repeated often. It might be beneficial to unify this into a reusable method for improved clarity and maintainability.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -379,22 +381,34 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
           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.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
 
-        if (existingWhitelistRecord) {
+        if (existingWhitelistRecord || existingGlobalWhitelistRecord) {
           const toBeAdded: string[] = [];
           address.filter(ele => {
-            if (!existingWhitelistRecord.addresses.includes(ele)) toBeAdded.push(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 });
-          const allAddresses = toBeAdded.concat(existingWhitelistRecord.addresses);
-          existingWhitelistRecord.addresses = allAddresses;
-          await server.whitelistRepository.updateOneById(existingWhitelistRecord);
+          if (existingWhitelistRecord) {
+            const allAddresses = toBeAdded.concat(existingWhitelistRecord.addresses);
+            existingWhitelistRecord.addresses = allAddresses;
+            await server.whitelistRepository.updateOneById(existingWhitelistRecord);
+          } else {
+            const addWhitelistDto = {
+              apiKey: api_key,
+              addresses: toBeAdded,
+              policyId: policyId ?? null,
+              epVersion: EPVersions.EPV_07
+            }
+            await server.whitelistRepository.create(addWhitelistDto);
+          }
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Consider concurrency and partial insertion.**  
You are merging addresses across a global record and a version-specific record. In a highly concurrent environment, separate requests could lead to race conditions. If concurrent updates happen, one might overwrite the other's changes. A more robust approach might implement an atomic operation at the database level or ensure an up-to-date instance is re-fetched after each update.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -457,21 +471,38 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         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.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
+        const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        let isGlobal = false;
 
-
-        if (existingWhitelistRecord) {
+        if (existingWhitelistRecord || existingGlobalWhitelistRecord) {
           const toBeRemoved: string[] = [];
           address.filter(ele => {
-            if (existingWhitelistRecord.addresses.includes(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 (existingWhitelistRecord.addresses.length < 1) await server.whitelistRepository.deleteById(existingWhitelistRecord.id);
-          else await server.whitelistRepository.updateOneById(existingWhitelistRecord);
+          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);
+            }
+          }
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Check for concurrency in removing whitelisted addresses.**  
Like in the whitelist addition logic, concurrent requests could lead to partial or inconsistent updates if the same addresses are being removed. Consider employing transactions or re-checking records after the remove operation.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -535,12 +566,14 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
         }
         const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? '', SUPPORTED_ENTRYPOINTS.EPV_07);
         if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK });
-        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        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 })
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _🧹 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.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -612,6 +649,316 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
     }
   )
 
+  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 })
+      }
+    }
+  );
+
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _🛠️ 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.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -612,6 +649,316 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
     }
   )
 
+  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 })
+      }
+    }
+  );
+
+  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 })
+      }
+    }
+  );
+
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _🧹 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.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -612,6 +649,316 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
     }
   )
 
+  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 })
+      }
+    }
+  );
+
+  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 })
+      }
+    }
+  );
+
+  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 })
+      }
+    }
+  );
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _🧹 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.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -612,6 +649,316 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
     }
   )
 
+  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 })
+      }
+    }
+  );
+
+  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 })
+      }
+    }
+  );
+
+  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 })
+      }
+    }
+  );
+
+  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 })
+      }
+    }  )
+
path: backend/src/routes/whitelist-routes.ts
user: login: coderabbitai[bot]
body: _⚠️ 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.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T03:22:44Z
author_association: NONE
diff_hunk: @@ -21,6 +21,8 @@ import { CoingeckoService } from '../services/coingecko.js';
 import { Sequelize } from 'sequelize';
 import { abi as verifyingPaymasterAbi, byteCode as verifyingPaymasterByteCode } from '../abi/VerifyingPaymasterAbi.js';
 import { abi as verifyingPaymasterV2Abi, byteCode as verifyingPaymasterV2ByteCode } from '../abi/VerifyingPaymasterAbiV2.js';
+import { abi as verifyingPaymasterV3Abi, byteCode as verifyingPaymasterV3ByteCode } from '../abi/VerifyingPaymasterAbiV3.js';
+import { EPVersions } from 'types/sponsorship-policy-dto.js';
path: backend/src/paymaster/index.ts
user: login: nikhilkumar1612
body: please correct the import here @vignesha22 
created_at: 2025-04-07T09:44:31Z
author_association: MEMBER
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
path: backend/src/paymaster/index.ts
user: login: nikhilkumar1612
body: is it expected to use EtherspotAbiV07 here ?
@vignesha22 
created_at: 2025-04-07T09:47:01Z
author_association: MEMBER
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
+      if (!userOp.signature) userOp.signature = '0x';
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
+      if (!userOp.initCode) userOp.initCode = "0x";
+      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
+      if (estimate) {
+        userOp.paymaster = paymasterAddress;
+        userOp.paymasterVerificationGasLimit = this.EP7_PVGL;
path: backend/src/paymaster/index.ts
user: login: nikhilkumar1612
body: is it okay to reuse EP7_PVGL property or do you think we should have a separate class property ? @vignesha22 
created_at: 2025-04-07T09:48:09Z
author_association: MEMBER
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
+      if (!userOp.signature) userOp.signature = '0x';
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
+      if (!userOp.initCode) userOp.initCode = "0x";
+      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
+      if (estimate) {
+        userOp.paymaster = paymasterAddress;
+        userOp.paymasterVerificationGasLimit = this.EP7_PVGL;
+        userOp.paymasterPostOpGasLimit = paymasterPostOpGasLimit;
+        const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+        const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+        const packedUserOp = {
+          sender: userOp.sender,
+          nonce: userOp.nonce,
+          initCode: userOp.initCode,
+          callData: userOp.callData,
+          accountGasLimits: accountGasLimits,
+          preVerificationGas: userOp.preVerificationGas,
+          gasFees: gasFees,
+          paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
+          signature: userOp.signature
+        }
+        userOp.paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
+        const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]);
+        userOp.verificationGasLimit = response.verificationGasLimit;
+        userOp.callGasLimit = response.callGasLimit;
+        userOp.preVerificationGas = response.preVerificationGas;
+      }
+      const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+      const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+      const packedUserOp = {
+        sender: userOp.sender,
+        nonce: userOp.nonce,
+        initCode: userOp.initCode,
+        callData: userOp.callData,
+        accountGasLimits: accountGasLimits,
+        preVerificationGas: userOp.preVerificationGas,
+        gasFees: gasFees,
+        paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
path: backend/src/paymaster/index.ts
user: login: nikhilkumar1612
body: same as above
created_at: 2025-04-07T09:48:30Z
author_association: MEMBER
diff_hunk: @@ -29,6 +29,7 @@ const ConfigSchema = Type.Strict(
     EP7_TOKEN_PGL: Type.String() || '150000',
     EPV_06: Type.Array(Type.String()) || ['0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'],
     EPV_07: Type.Array(Type.String()) || ['0x0000000071727De22E5E9d8BAf0edAc6f37da032'],
+    EPV_08: Type.Array(Type.String()),
path: backend/src/plugins/config.ts
user: login: nikhilkumar1612
body: fallback value `['0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108']` is not needed here ?
created_at: 2025-04-07T09:50:26Z
author_association: MEMBER
diff_hunk: @@ -1,6 +1,7 @@
 import { Sequelize } from 'sequelize';
 import { APIKey } from '../models/api-key.js';
 import { ApiKeyDto } from '../types/apikey-dto.js';
+import { EPVersions } from 'types/sponsorship-policy-dto.js';
path: backend/src/repository/api-key-repository.ts
user: login: nikhilkumar1612
body: please correct the import.
created_at: 2025-04-07T09:51:12Z
author_association: MEMBER
diff_hunk: @@ -468,5 +468,15 @@
         "thresholdValue": "0.016",
         "MultiTokenPaymasterOracleUsed": "chainlink",
         "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
+    },
+    {
+        "chainId": 11155111,
+        "bundler": "https://testnet-rpc.etherspot.io/v3/11155111",
+        "contracts": {
+            "etherspotPaymasterAddress": "0xEd5371296A2030F53298a18a3Eb828cFc3dca73f"
path: backend/config.json.default
user: login: nikhilkumar1612
body: what is `etherspotPaymasterAddress` here ? if it is verifyingPaymaster address deployment then we have to remove it even from v7. @vignesha22 
created_at: 2025-04-07T10:30:55Z
author_association: MEMBER
diff_hunk: @@ -18,7 +18,8 @@ const metadataRoutes: FastifyPluginAsync = async (server) => {
 
   const SUPPORTED_ENTRYPOINTS = {
     EPV_06: server.config.EPV_06,
-    EPV_07: server.config.EPV_07
+    EPV_07: server.config.EPV_07,
+    EPV_08: server.config.EPV_08
   }
path: backend/src/routes/metadata-routes.ts
user: login: nikhilkumar1612
body: as suggested by coderabbit, please implement metadata/v3 endpoint @vignesha22 
created_at: 2025-04-07T10:51:10Z
author_association: MEMBER
diff_hunk: @@ -254,13 +255,26 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
               }
               if (epVersion === EPVersions.EPV_06)
                 result = await paymaster.signV06(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
-              else {
+              else if (epVersion === EPVersions.EPV_07) {
+                if (!networkConfig.contracts.etherspotPaymasterAddress) {
+                  throw new Error('Please use useVP flag to use your deployed verifying paymaster as global paymaster is not defined');
+                }
                 const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
                 if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
                   const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
                   if (!existingWhitelistRecord?.addresses.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
                 }
                 result = await paymaster.signV07(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
+              } else {
+                const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
+                if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
+                  const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
+                  const existingEpWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion, sponsorshipPolicy.id);
+                  const existingEpWhitelistRecord2 = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion);
+                  const combinedList = [...(existingWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord2?.addresses ?? [])];
+                  if (!combinedList.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
+                }
+                result = await paymaster.signV08(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
path: backend/src/routes/paymaster-routes.ts
user: login: nikhilkumar1612
body: why are the epv7 and epv8 code blocks different here @vignesha22 ?
created_at: 2025-04-07T10:53:19Z
author_association: MEMBER
diff_hunk: @@ -379,22 +381,34 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
           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.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
path: backend/src/routes/whitelist-routes.ts
user: login: nikhilkumar1612
body: why is there a change in logic to add whitelist now ? @vignesha22 
how is it decided if the address has to be added to global whitelist or a specific whitelist record ? 
created_at: 2025-04-07T11:02:56Z
author_association: MEMBER
path: backend/src/routes/whitelist-routes.ts
user: login: nikhilkumar1612
body: can we please move the logic for v2 and v3 endpoints to a common function as they are exactly the same except for their ep version.
@vignesha22 
created_at: 2025-04-07T11:05:10Z
author_association: MEMBER
diff_hunk: @@ -262,7 +262,9 @@ const initializeServer = async (): Promise<void> => {
                     ) {
                       const thresholdValue = network.thresholdValue ?? networkConfig.thresholdValue;
                       const bundler = network.bundler ?? networkConfig.bundler;
-                      checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                      if (network.contract.etherspotPaymasterAddress) {
+                        checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? '0.001', Number(network.chainId), server.log);
+                      }
path: backend/src/server.ts
user: login: nikhilkumar1612
body: as suggested by coderabbit please change `contract` to `contracts`
created_at: 2025-04-07T11:07:06Z
author_association: MEMBER
path: backend/src/types/whitelist-dto.ts
user: login: nikhilkumar1612
body: if we are adding a new column to store ep version, i think we need a migration script to add data to existing policies @vignesha22 
created_at: 2025-04-07T11:10:09Z
author_association: MEMBER
diff_hunk: @@ -468,5 +468,15 @@
         "thresholdValue": "0.016",
         "MultiTokenPaymasterOracleUsed": "chainlink",
         "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
+    },
+    {
+        "chainId": 11155111,
+        "bundler": "https://testnet-rpc.etherspot.io/v3/11155111",
+        "contracts": {
+            "etherspotPaymasterAddress": "0xEd5371296A2030F53298a18a3Eb828cFc3dca73f"
path: backend/config.json.default
user: login: vignesha22
body: These are the VerifyingPaymaster addresses which would be used for etherspot_public_key 
created_at: 2025-04-11T06:46:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -468,5 +468,15 @@
         "thresholdValue": "0.016",
         "MultiTokenPaymasterOracleUsed": "chainlink",
         "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
+    },
+    {
+        "chainId": 11155111,
+        "bundler": "https://testnet-rpc.etherspot.io/v3/11155111",
+        "contracts": {
+            "etherspotPaymasterAddress": "0xEd5371296A2030F53298a18a3Eb828cFc3dca73f"
path: backend/config.json.default
user: login: nikhilkumar1612
body: so why do we need them in config ?
it will be used according to the key used correct ?
created_at: 2025-04-11T06:56:37Z
author_association: MEMBER
diff_hunk: @@ -21,6 +21,8 @@ import { CoingeckoService } from '../services/coingecko.js';
 import { Sequelize } from 'sequelize';
 import { abi as verifyingPaymasterAbi, byteCode as verifyingPaymasterByteCode } from '../abi/VerifyingPaymasterAbi.js';
 import { abi as verifyingPaymasterV2Abi, byteCode as verifyingPaymasterV2ByteCode } from '../abi/VerifyingPaymasterAbiV2.js';
+import { abi as verifyingPaymasterV3Abi, byteCode as verifyingPaymasterV3ByteCode } from '../abi/VerifyingPaymasterAbiV3.js';
+import { EPVersions } from 'types/sponsorship-policy-dto.js';
path: backend/src/paymaster/index.ts
user: login: vignesha22
body: Done

created_at: 2025-04-16T08:15:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
path: backend/src/paymaster/index.ts
user: login: vignesha22
body: yes its the same but yeah I see your point. Have added it separately as a separate file
created_at: 2025-04-16T08:16:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
+      if (!userOp.signature) userOp.signature = '0x';
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
+      if (!userOp.initCode) userOp.initCode = "0x";
+      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
+      if (estimate) {
+        userOp.paymaster = paymasterAddress;
+        userOp.paymasterVerificationGasLimit = this.EP7_PVGL;
path: backend/src/paymaster/index.ts
user: login: vignesha22
body: Added a separate config var
created_at: 2025-04-16T08:17:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -178,6 +180,80 @@ export class Paymaster {
     }
   }
 
+  async signV08(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
+    bundlerRpc: string, signer: Wallet, estimate: boolean, log?: FastifyBaseLogger) {
+    try {
+      const provider = new providers.JsonRpcProvider(bundlerRpc);
+      const paymasterContract = new ethers.Contract(paymasterAddress, EtherspotAbiV07, provider);
+      if (!userOp.signature) userOp.signature = '0x';
+      if (userOp.factory && userOp.factoryData) userOp.initCode = hexConcat([userOp.factory, userOp.factoryData ?? ''])
+      if (!userOp.initCode) userOp.initCode = "0x";
+      const paymasterPostOpGasLimit = BigNumber.from("40000").toHexString();
+      if (estimate) {
+        userOp.paymaster = paymasterAddress;
+        userOp.paymasterVerificationGasLimit = this.EP7_PVGL;
+        userOp.paymasterPostOpGasLimit = paymasterPostOpGasLimit;
+        const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+        const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+        const packedUserOp = {
+          sender: userOp.sender,
+          nonce: userOp.nonce,
+          initCode: userOp.initCode,
+          callData: userOp.callData,
+          accountGasLimits: accountGasLimits,
+          preVerificationGas: userOp.preVerificationGas,
+          gasFees: gasFees,
+          paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
+          signature: userOp.signature
+        }
+        userOp.paymasterData = await this.getPaymasterData(packedUserOp, validUntil, validAfter, paymasterContract, signer);
+        const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]);
+        userOp.verificationGasLimit = response.verificationGasLimit;
+        userOp.callGasLimit = response.callGasLimit;
+        userOp.preVerificationGas = response.preVerificationGas;
+      }
+      const accountGasLimits = this.packUint(userOp.verificationGasLimit, userOp.callGasLimit)
+      const gasFees = this.packUint(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
+      const packedUserOp = {
+        sender: userOp.sender,
+        nonce: userOp.nonce,
+        initCode: userOp.initCode,
+        callData: userOp.callData,
+        accountGasLimits: accountGasLimits,
+        preVerificationGas: userOp.preVerificationGas,
+        gasFees: gasFees,
+        paymasterAndData: this.packPaymasterData(paymasterAddress, this.EP7_PVGL, paymasterPostOpGasLimit),
path: backend/src/paymaster/index.ts
user: login: vignesha22
body: Done
created_at: 2025-04-16T08:17:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,6 +29,7 @@ const ConfigSchema = Type.Strict(
     EP7_TOKEN_PGL: Type.String() || '150000',
     EPV_06: Type.Array(Type.String()) || ['0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'],
     EPV_07: Type.Array(Type.String()) || ['0x0000000071727De22E5E9d8BAf0edAc6f37da032'],
+    EPV_08: Type.Array(Type.String()),
path: backend/src/plugins/config.ts
user: login: vignesha22
body: no since on line 116 thats where all the default values should go
created_at: 2025-04-16T08:18:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -468,5 +468,15 @@
         "thresholdValue": "0.016",
         "MultiTokenPaymasterOracleUsed": "chainlink",
         "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
+    },
+    {
+        "chainId": 11155111,
+        "bundler": "https://testnet-rpc.etherspot.io/v3/11155111",
+        "contracts": {
+            "etherspotPaymasterAddress": "0xEd5371296A2030F53298a18a3Eb828cFc3dca73f"
path: backend/config.json.default
user: login: vignesha22
body: no since if useVp flag is off then it will use this
created_at: 2025-04-16T08:19:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -254,13 +255,26 @@ const paymasterRoutes: FastifyPluginAsync<PaymasterRoutesOpts> = async (server,
               }
               if (epVersion === EPVersions.EPV_06)
                 result = await paymaster.signV06(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
-              else {
+              else if (epVersion === EPVersions.EPV_07) {
+                if (!networkConfig.contracts.etherspotPaymasterAddress) {
+                  throw new Error('Please use useVP flag to use your deployed verifying paymaster as global paymaster is not defined');
+                }
                 const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
                 if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
                   const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
                   if (!existingWhitelistRecord?.addresses.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
                 }
                 result = await paymaster.signV07(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
+              } else {
+                const globalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key);
+                if (!globalWhitelistRecord?.addresses.includes(userOp.sender)) {
+                  const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, sponsorshipPolicy.id);
+                  const existingEpWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion, sponsorshipPolicy.id);
+                  const existingEpWhitelistRecord2 = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, epVersion);
+                  const combinedList = [...(existingWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord?.addresses ?? []), ...(existingEpWhitelistRecord2?.addresses ?? [])];
+                  if (!combinedList.includes(userOp.sender)) throw new Error('This sender address has not been whitelisted yet');
+                }
+                result = await paymaster.signV08(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, bundlerUrl, signer, estimate, server.log);
path: backend/src/routes/paymaster-routes.ts
user: login: vignesha22
body: added a common fn for checking
created_at: 2025-04-16T08:19:36Z
author_association: CONTRIBUTOR
path: backend/src/types/whitelist-dto.ts
user: login: vignesha22
body: no I think the user should enable them after this 
created_at: 2025-04-16T08:20:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -468,5 +468,15 @@
         "thresholdValue": "0.016",
         "MultiTokenPaymasterOracleUsed": "chainlink",
         "entryPoint": "0x0000000071727De22E5E9d8BAf0edAc6f37da032"
+    },
+    {
+        "chainId": 11155111,
+        "bundler": "https://testnet-rpc.etherspot.io/v3/11155111",
+        "contracts": {
+            "etherspotPaymasterAddress": "0xEd5371296A2030F53298a18a3Eb828cFc3dca73f"
path: backend/config.json.default
user: login: nikhilkumar1612
body: But it will work only if user uses etherspot_public_key, so what is the point of having default paymasters for ep7 ? 
created_at: 2025-04-16T10:23:47Z
author_association: MEMBER
diff_hunk: @@ -379,22 +381,34 @@ const whitelistRoutes: FastifyPluginAsync = async (server) => {
           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.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingGlobalWhitelistRecord = await server.whitelistRepository.findOneByApiKeyAndPolicyId(api_key, policyId);
+        const existingWhitelistRecord = await server.whitelistRepository.findOneByApiKeyEPVersionAndPolicyId(api_key, EPVersions.EPV_07, policyId);
path: backend/src/routes/whitelist-routes.ts
user: login: vignesha22
body: Done moved to common fn for checking whitelist
created_at: 2025-04-17T09:33:27Z
author_association: CONTRIBUTOR

PR comments:
user: login: linear[bot]
created_at: 2025-04-02T03:16:10Z
body: <p><a href="https://linear.app/pillarproject/issue/PRO-3097/arka-support-for-ep8">PRO-3097 Arka support for EP8</a></p>
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-04-02T03:16:16Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!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.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- walkthrough_start -->

## 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)

```mermaid
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
```

```mermaid
sequenceDiagram
    participant C as Client
    participant S as Server (/whitelist/v3)
    participant W as WhitelistRepository

    C->>S: POST /whitelist/v3 (api_key, address, epVersion, policyId)
    S->>W: Query findOneByApiKeyEPVersionAndPolicyId(...)
    W-->>S: Whitelist record / null
    S->>W: Create/Update whitelist record
    W-->>S: Operation result
    S->>C: Send whitelist update response
```

## Poem
> 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!  
> 🐇🌟

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpADK2VmgqyjcEj2wAbb5AokAI7Y0riQ4rjedJAARLYA8rqQAMwADACcAOxJAII2ANI5kCTcEqkAHA7Y3Nz4FLgxkADuaMiI2ALM6jT0cuGwntiIlJAS8ETkiLBoAEwz6Bj0qPBKGOIAZvDRfbgDkBhOAiMAjOnlGjB7vPDMLvLr+AxD/Ov9qLYoyAQoaxSK2AxPO0anUwg8+ABRKwANQqzXUCCwu08eUKPD+IjEFyMAEkkXslIgGBR4NxxPgMAAaFBhVAYfA9frUfqeBjTDCkZC3JQ/BjebA85HoWi0dTwCkvSBQ2GVYG1erUpoINmfRjUEhEOrwABe0Va6H2JCakHWJGo2ACFw81jstHw0n2DN5/J5mHkAmwRBN8AAHtJqQIAmgANbwDmMdmc6l2p5sNbUcVYaq0dWIalMHmIXCyKKQZOp6kBdZoMRajnUuqQD3wby0MABbzq+hszCc7EGa3cHx+ALBUKqtASbSNgS58H+EhjI3oMJCh6+fBNMNegCqNgAMlwANqwXC4biIDgAeiPRARHQ0TGYR5IyIoiFquCPLmDaCPXd8R9O5QAugAKXd90PE8z12C8rxvO8HwZZ8KFfd9uy/M4AEoNCMfRjHAKAyHofBXjQPBCFIcgqEZK8404dF+GEURxCkGR5AzZRVHULQdAwkwoDgZZkEwHACGIMhlDIlgKK4KhjXaZxXCrRjFGYtRNG0XQwEMTDTAMAQS2DHCjwAYQACRyAA5ABxCF13iUyNGYWgOAMGJHIMCxIByHFBJIptICk24ZLwiNW2kdw9hbDkSG8fAvWmGQSDIPNuBTRlvjDPkBU8NBDWNKR70TYpfnuStssQXLkg0GYNFSaMvJya4/GOakZlSGYAFYrQQZB2BksNcD+WgAQdDLyGNU1zQCSAR3C6IYmlOFHBBeoNEaHIRTFCk0F8WRqSFXhJ3FZ4ityzqCr4Urjg0ZqqsZABZFwVRmAAWBqmouyBoqrWKsACXBtHIWhFWVWBnTS3jvR9CdGyS/BwnwHSsHZWhvGXH4WUgZgfHEaAYbIKw0FkW4s0oNrWUjB1rl8ms5IwYqs0lIU0BW8ksH8wbp2GMRcvHIUIf7A6JUweghRS/liolZmqxJEhXkJYlSUZ2m9iGk0zVwC0SDQoxnMsHJvBoUjEy+KGhSUPkXATClkH8kgfXlRlKy7Ud4AYPKIi2RB0MgYzpyO4UlDsyAAAMXK3UrytSH8kkalqwFSe7o+Of3kf9rSGFh2h9KMsyLKsmzaH9gwoE94bldVn26C4f2oGW32pRhWbqhthOwwD5PU/TkzzMs6zbLzxyYnQjSW90pgME2IgNCERAKQ0JRi3R+ze811z3OI4Toh8u5JVCzlgs8UV1lebrev6kHFeH0eLTNrBvc5kLpibnEABEA+OF/mual/47a1BvaF4GDST7AiwogUATmudcPB8DdSRt8f2gEDzHiPDQLM5BcD1m4AwDQt4Bj3kfBocUR4JDJC/K/d+L9/bUgyv7YePUSy4EQAnQQmIwhLl2AsAOWDKDQVwDjPGrRdZVwCIgeh3lbzQwDqkH0EJaDNWSJkY4Mx0gADYciNTSAAMRkQo8oaBjjaOSBCAQ5QZjlAYGohgyRaAMDQJkZI6xyH/12II2A+BazQnWiEBhrx/apAqscRR9jKFXXRvATGsMeH411vEKgfISArmGLnUY7jPD+SoXfDAiMMDBgCYsNh/sjpWEgWsBO9NaCCItl4iR91kiyIqPdB+6QIQtWamo9YqRMhNXuoo44ek1HlHKOkZqekIQNOSAIZp6xjgVH9l/XiDNcqoG5OlCKHJiquivj6JA4hwzX0rEKRAaA2ABXvg/f6jtAYLMUPATY0RkoYFSq6TKzdAEI0oKAjcCx6AZT3qaAIax2H5QKd1YpIoylE3RLtfAzwdl8BbMc5+JCP4J3pMaN66hkAAKAa8+KiUblQ1gXueBJ4kG4BQWgjBHCcEMjwfgAhMxiHHDfoij5E5vphmQGi8FYxIXoopVw8JfDKACOkPQ6k/tHHSGca4pJ2T6D+yCTrEJWMMD8oJhQKJJYohxLoNMjWi9ta60vgbFGxtGx63NpKK2Ntoh2w6IjJ27AxRBXzh7acZ9xgXzlt7Ep0RxywqwI/eFDLSHx3hKwucLiIpLnDEoVl3hDwGEgEkDFLyQHlxiHA4CiDQikooOgzBUFcH4MIfSxlL8Yh50TVAKhFIaFiHoVwBNiak28sfCq/hILhX+zTRIqRMi5EKOUao1IGjkhaJ0XogxRiTFmIsVYmx6xy2NqreKqYLjaBuP5CQLtAcYg+NSH4xdlaA7yoxkqttlB1UxK1bnNNfqMnBkPc2gFhTcDbv9run0VSanlDqQ0ppLS2kdK6T0vpAyhkjLGS0yZ5RF0L3zgPbSukuhEDNVTI8kdmqpFHeUVIuH91gHzDQMAaBuDwHyCQWQYBvqjjVgwCe88nIuTch5Ve9B15+VeFvJ13FkDfJ+D1f4gIT7TmQ6h70Y5KyEegXsf2JH4AAH0dKyGEdR3MTcvnUBUK0QYxVwz2CCCERGuowX+2qAndYgD2Z8xFMJ40TB+TMCwBgA50R/bQghDYHEaiACaOIzLyasDkbzV0cj2GgB5+w8noTJE8eEWQ3BPD+3CwADWgAnb4QpZOkcUxRlTKgogUIXFGr0BxfCJM3YgEzdomgYHM5ZuWvB8BjEJCjCzdzGbrXUPIb4ARmBNc8LsVA9mnBYCuYaOgdALgACEGSAza1ZqmeZxBGYGzJvsrg8S62LICdLUNurKDEKGwGRtNNaWGN5NkJBbjeQSwwK5Wxejdb2GQMYfwMAUUSSSfLiWH45GgDkKboWITyfsIZCEIX5PGRyFdCE0zLieFE5fVUlrQR6hBn1vqEm+BDE8CwwG+m+wrcgHpdcOJ1bmC1jrYS+sxFG1EKaw1Frrao9wnwe2drnaOrds6q6WDFClz9rJxAsg7kmnq7laof4ADeCg1hW0out2Qm3KDbc8AAX2Qo3LASdEOLCPIjxmiB0PPSw8YvD+HCMkGI6R8jlHVO0YnnnKAvPdj8+9YL1oIunbzbltVjA0vZc0B9ArkIG25cUFV5ADXWvm667Tgb/WxuWqm5w3h44BGErqmt2RijVHvuXkdw5Jy8GwBGEHnrhP5sk+Yew+bmYGecXEbgmgMASp1DhU2XnmjBf40LyY8vISpE15OF8vIfyXHuc8cgHxw+gmBqPMr1gTYWPsUJnDJll8aB5Nt5oIjLM6XvvIxO9R7TJmzNi/a7lEptmFAOacy52VUIosRZxPEYysXswJYDmFzzZldso1k2b23wQF302QPxo0K0jSRlKz8CHAqyq0XFqwvwW3RCaxWAdCFCsW4BUBrC6zEV636wAKf3cxsHsFf3f1vxG2pEllNHZikG8HkACAOnX1vkCi5Hpk8B2Bk3Px90TGm1m2QMNyW1wN1AAMV2VwjxLC3VQOa22HkAJ0Mx1AGz23D1oSO2NVO20wuwGGuwfFEHu2iCbiFBe3gDew+yHC+xowDl+3+0B3sGB1BwMnBxyEh2h1hzBUX2R2Z3qDRwNAxx8E8HHBx3UIUI71EJJzJ11SYyp1QyNTpxNliKZytVZy8AdntTWC53dhd2cU+RFDLgDk91F14IlElxl2oXly4HEPD0j2j0TnL3j3GFiOrxTzrwbyz031bxAI7yzC7yiB7yd0gGyLdzyI92FyKPFwlD9wD3KOD0qNDyV2qKkKj01zqLj310aMNWaNrzw3r0tyb1fE6Pbz31QXt36KLz7hLzLzWOwJTjQFIHHkngwAYz7n7xYyHzYxHw3nHxJknz2F5iZleG2m0juM8DeiODikt3oHWD+GYFiFOnOkaG+BiBDgqkaCMJk3qPfGBPuIngpHMxrDVkuCGxJgnHWCiDrQNGGFuAyKdn+KRhSl63YGpDDFFCsS2S9F2GZDvAR1xnekeRGhVkEWZOYEaykAojTH4D4E2D9HZXxEGEz0ZH+PJz1RiMZwywJHp1NiEMtm8NtjZ1tUdk53ECdSgGhE4QlwVPyPfX+JiC4GRI0DOmanLVWJTl0huNfBxMeITkACTCHdG0u0lE1IZ0puHXV0vXd0kEh4vE84/uUvTSNYxACgBgZ8NQI8M0kkdYWQZcc9CgHINQaLDQOhZ4vVAfTyRkdjMfTjH4neGQ9A+gGfX4OfG/aAeLEgewGWMkcTEgUVdMq5LMjkHMvM+AAsuhexNvFUFHeoXiLAAHHESAP8GqbgO1JHKbMMDeCQ1XZCZlOQGgJiE0SsDKRAXyMIahaJTQeHVyKbOc4eamOhSUCwvaZAK8WocgNYWU+5JGDKG8nqAEAgCgakNGBVJczwSgP4PgGeMMVaKmagqQN8ihHJYoqmMFb8igX83ZEMDApoKGbAqgNgXWQ8AOeTfJF9D/VsgojtIRYpHJf2eTbKPs5cewcYEiUir/WTCi+hdsa0UCuoXjSWMMQwu5F0RLCEPSB+ewNyDAOAlYRiiYUaLdUVESsSiSqS2gGS5zQUkgdcMgIgXYexf2RS8SvEFStSuS+wPS+IGrb7IyzrWgCy5i+C2VOy77FcZzPAZxEkXUWgHIBgJgQBV9agksQGPHG7fQzYJ2MMLsWcVsyrGALC4oWCu8lwXePi36cuOyzhBAbgaAKgKmH5AIXOU5FUWtYMJ8n45GRAjK0kBygOXszMhipiygFcS0gq5oAGcaSKZAS3OIv4ygei8MYqCYEYEpUFGsxC98oSpYOUy868/rPgDKHK0gSURCzrY0toAEQGfUEpJGLMDC6MEofAYqdkwsW8CWMYFgkC/KCBbqakUgPcJGJQWoQ6iUnC3TL0bA3hVVafTTaqlStfL0HHPgfABLWIi4YyBkfLTaAA26gyVoWAOrS/PmHykoRKgOHGV02gLVNVYGy+BOLMVCw7fmcIOK0zbqe6coBOHClzfC/cvgFSvAy7FOGK60f2R63AeIbgeGlA+GKIZAVmsAIG6nCUCKM8J2YKymvCzhGm1GeSdMGteXaq2hbAdaSAIgfUJgLMGKowdK+8TK1GTAEEj7VAbbXA76JKWAP4T0ObCYxbRGHSAOX5SFO5EgbWqYUkKi2VGhXKi9GrSqjm5CmtM89AONFQ+5B0MaqW6ku47a76HSCU9oFUfUf2PHUpNAJoewGO6QwmpOhEFOpoTGAJIrJGG+XHHOiSZW/yNrWgJmvYHc0QeSKWzAgOtQ1AdaSeWs32Qq85EGAYH0Dgu7W4HsEoQRdgJHfyRukUgk5sJurELW7KdabwLadqLskRCgeidAEGZ8gIAYKmSQTwfbSQwEKW/e2hJGYKvZY8wPM8va2Neku8vG381WagigMC16fmDJIgaqgW1DZWsa5U6Ig1IQ9U5KhIxnHU5IyU1IjnB1Va92QuYoXU6Ib8zASiWTNQFixLb8mPUM1uRM5MnAtM3quqgc3GCJQVfM0qUc51OByckSKmb6NYcuWuvSeSdBgOTBl0nBpMlM+AAhjM/sogQc8hws+hGMy4+MsMtOXBo8JBt8o8E9dQM9EhgVCgYR4s14led47yT4jjAKMKX43eK5A+RsvqITA0RWb2IBgOUJMgB+fQgexAHIRYewPGAQFxBOGR2kKauVYJAgMJJR1VYR/EvohwW7e7KxDaZksIa/Mx6cXxuKM+vYYa4VAOAAch7VilighAyayayZIBybycyYhFycyYydKaKbyZSbHLarei+TseVrgJCElF0WZRbqEUeHgC8gSaBBcZcUgBSYhGgAMhSZmR9igv4B8otF4nWF1jYStk2SRmhRRjiawC6Z9jKVSYkXSDoEyAhHuim2OCmzUTUXSGSHSHpnuhsS8uaghEyFmF0T0kyDkQfgZUUXSHumakUUqc7onB2mGDfKWaVT6ZsAhBXGMhxBSfUMUWnzqbjWQpJLtAdHpCif3logWHkDmazAWd+FdggeRFMPKxCAlI9FpDCEtX5i+D2FuBqCRjHsBaSaEQwMNgGHxcEVuzomAZuHqaSWnLYx6dhaiMpwAZp0sZNS1JpzAZZwgfZ0NOgddlga9kuvd3LjSckTKeyYKYKfybyfKfVdKcKYqa4Bl2Nk5bjS4F0WpGF2YFce8C4H6cGYhbVywesYwFsf7tbscdUr5awcxKkY8aN3keWZzMCdEYgDjJ9a4Yx3CiNzkzACU1UZjPUcHy8grM3mrI7FYL0f4yPkPpZmNCBo6z8Ea2BuzADjoqIYEf8fwui3/0yxyCsBxFt3ccbCEUgMXFvrEUlU+W8h6hpb4BgIJcJKny2rllQCLHJMZCblcbDRCmbd4s2AwDGcJsFgXfEE621CR0QoDkgtXZWzrYbYoyunkm8DhzxBRm3Y6aMw3etooQUGfukBfNFG2UuvHH9jLf4ZzMQGrYHBGNp0paPZSd4j3BJGJf9BCru3LaZBJdVDHrIv9gfk0xbIS0qxSzS1baaGQH7YadA8Jqpe6t3k0PO2uVrBqo8y818380C2C1C3C1IKixi1BqhlmxGCLcoFWogeFsNOmCkHejigxwMJSIIKkFoD/sFcFsWxFc1MSIlZ8JSOlfSJdhNOsD+GLfkCVYDjk9GEIffcrc4WiwAH4uA8akYAAffYbsAAbhjxNiEQDj3cbY4d0ikcjbjWfFI1jYo2DedyPZnB7ZA4F3LjfezJ0/vGi0NbiwSy4Hg++kQ+kA0BQ7Q+Mm7C4B/O7O9HCj9hSZILI781MgCyCxCzCwizo4dZj03f9nPbXZIDs4PaPe9YTIjaPejbc7jcobg1DauIkaPCc8a6PB326M0CLITa1lLNYy0ekkrN0e3nTeJjYKzbnxibzbJETGVpY/qHkDyW4HTJFiQMsf9hyGbwAHUujjim3WgeWaQny6hWWKRH2vREotNztnPoX52oKTMSgtvEwKblPWP5BUAILfp17KSe2WDmQrEsBW6oYjgzPfBmSV2L2lCBYoZ+2+gZ4CIdYLhT2hQnuKvL25YyucelD9vXwjujjNlD2lBj2b3FYiP6AqXSNwwX33vzS8Tv2O6wO+qOTpgoPUAYPWKou0AYvKsf8cuC7I0MPuwB2JScOSM8OvqT9zthtHMA5iCX8384ch25kJQyB2RTHj9DyfyxAS4+h5623zru26gQTJQ+Jh3lvYDmeF3wxqBfO8BPAGwvJLG+vjjnYJZKsBXXJVTAGmXgGGdtTXgaHrV9S0ijS5XnUrBvu1v/P1ODT7VNv7eDPu2SRwxTP+2ABeaH7wSz5Gaz9FIntAEn0A/fBzvXbrino3T3zZTzyANRLYYjtTjbj7ikMLz/EgSLhD6KjQYXsyBLpL8IVC1Lmn21lX0g8gkro/P9inn4MUNdpHEM8N5M5zuv47hv1r4vdr8Rzh5M960higI8Fkq2eNvvYbt45N7RibifGshsgTEx+fRWPCnIgOAajAGUGtmTHM07lt1qmcngY0BFgFLQItbRWoTdjCGyTFuGH9if9YQmQBOG/0UAmYEBUyVGHzkmqh1ZkooAtu1RFpS1uaSMAGvwGxqG5YeH5fqreHZLxQDQopdAnNRySnlaEAFfWmeHDCkCv6SOT/nJSl45JQgnLWgarWQCIwugdCDHp4yf7HwFuxQZ+pWGIHhg2AoUJALCU5JhA9C4HR2PPUYgDBGaUtDKDEAACK2AcGvA0BATZaAjQbinwDYBCJLehNYWh1HkH3gUA+EAgaQD4CoAGBvsDwlgKDpt0UKv5EGO6WiBcDyBuURhKi0JpfRTqpMYLnLzQAWsbgNYFwGIngFMVEB9iYluoW16YBAQ9AL6BaCwBYdX6QCJGGdmtQrMBgd4WZpi1uA0BVQvYQlj0HbBVwoKOgxeqyHNgG8/yPAU2BLT4DldEAEIbgKkH8Sv0YocUAIEuSkL0Bgq7fe3qw30owh7eIqQAQnSKzIBxwfWMaGSTmZWFFBXofyN8koDsBve8gWoN1E045RzY/tRYB0L8AcdvclYYYOO2kzpQagynL7I0JYGHZtsf5d0NplwhTVvYVwv5P8UmE8ckwzVMRCWHIiKB1QKMRWK+y05BcPqusaLEOXdqls0RxDDEZQGixTZZANAZhkoHV57AbgNsbtuqHFKv1uO4JLAHx2uSI8gYgoBWNOFnLMpu+z3c9ncNnrKAYeKMCfHlB16rZEsf/RgLOwA5qhsCagRGGx2N4rRHejyT/kjBQGfIckVIv4GdU56gk36NLMPpdXBFhB/ivvDWP/TE6y9RWUnMPgg1k7J9o+inIYrkV9jlxCiTsdAeUD/AA12aXAN0NSBUouVlshnYHh/XKwrAcg0zSgKGMz7hjiK3UWMcuGpBH9lGQqIREmPLBVhnkwCGwOgkzHhjP+MYyAAd3nq3hqC9Q9UFwFcYuIzQVIAgenzUR8I+yU2bTOuEiieCViTcYvqjWC51dOuUjVMaqlP6LBz+lDKADjFwq3gRgPIyEuXCQCjDxh1Y/ALWMwBWceh+NPoePlna9iCRICKvpIy4ZDjdYI4pQD6ECaQBfSiw24U8Rrgd8qY64uhpuMrDbizuu44/v2IP5Yk9xp4scSIza4IYBxR4/kBwKNxuox4g3S/kvGv7llb+qbNgjWRFGz5n+sg8CR6lyirdswClGEPJimRbQg+AcZhiPHGDtkdCaAHEZlhexuJ9xUQw7HjnRIYMKQo8KwCBLDCc1GYJmaULhPJqoEVOqof7r4Wt7P1eS/kIzisjn5AhLs12QmssDh6VdWR8I5GpLxNAwkUYJhMwucIfKH4Vh0IbiVUyAGoAHwCo2SAoGcAxV2hBbBgje1R7owB2UtHSXpNVA+CBKwopie6jEy0TcAFrURN8CElUBKYrKB3l6EPKhMwqGfZcCMxt5a8MAYosAbLnPhiYsCOBBUTi2N6F0GeB5cHpr2cx+B1JFIcwi4A6ZWFvgUPRrEJniTMo8AIhaIPRKmokYly2gjiX731RWjf2wfMVuamk56lIGMrDIjA1j7x8S2bfLiRUC4AxcNA+3fyX+HGnp04xf4ZCF2O1xETR4pEq7ORIPFddgJnoNlNIzckQSRGE4waapxGLlwRp5QLgGVOFSYJJKGgM6Xpw0BGT1Af4FJpSBSaLT/kEgaiQwhoiHZV+9XQ/qxLQzgTG+cfL+kNJOnK8cJo01AuVOukSBbpUM8oPdMem4Bnpr0rcnpz06QAtwKrL9O0h/T1JGkb8ADO0gejAZek/SQZMMghCjJxk0GFJj+CwbgTvpTCDaYOMBlgS9pwbACWG3+lHgZhB1BRq4Fc7wB3OlGAWYdTqCyAL+jGK/hoxv7jd4JejRCWVWQn9RZBVI0EPZOlBrDzMqksVNFS64vlJ4OtUkGAFqB2pKMtAAgA8RxHMinsiWS3NCG4DpjhgwiDURJNs71tbcNgfapLNcD/9q6COLATKN4EaVVQkJOEUjU7J8Rrx23CmgMOnGA1ASsHHWTePWEzDGwd2M3jtC5TPAaxUQPiOLSTlbsRhYw/xGCn3o5SCBhpMepS38Et0YiuKPMOdi/I3dHhGfB+mNHUERhRAJVFGKUJSRM8bxcOXEF4mHnxzVQOktYQjN0njD7E6gVfEggAKBd8Rx/YRDTwx7jzU+I8qeWnO26VYRpSAyJkvIwIyZV5FbPcZ+xmD4l0uFwNRJlLHy1DB528+8QvLCBdUV5eIy+evK/abyLyucx8igBFJRAKISOHuTZKpysipMcAi+R+2hA3zWqcUYYYuImEtAvgY/KEYyInB9YhO6seIHPSFGYEyqeQp2ijA1Eyj9hGyeUXgTSlQFwwi874NzQRzBJgKNwg+fLBRD1tIASmG4WEyRyXSbOlQkESjE0H8d2EO8+OSJ395CtzUEnEBqH3gbgMbUUfWVs6P8FhyS4c4gomMSdhOyXZ7FaQH+Dky24CxgYgxaUmFTSAzFnwVBZADz4pd3pPYvbt7Ioy+zHqQs2QJ+Mc5cMJZnikWWLPrB+zPFF4q8R6NPnOzXZRikxRRhsUSALFZSaxeFKzETzEwXAfefrCcU7iXF+7WQO4sFmAjvF1fXxcEsBEBKlMQSjxYCO5m79AJX4vxWUvr49EGlUsmWS8TllJtYJis74ghOm6TcHQasnNo8kwmQ045n3fCQAWJDKxpCHsuuYlnL79d8l/srxVKLO5od22vk6OVEyyl4Dbe/QqcTM2hIsAACMSlZZ5JGYjLVQwwWcHihgKNxXgSLdupNib4Wg7wuw1Lrm0wGu4/oAcedrZXIDEiaoOeWQBkopAesCklsnEC1Rbo/tLGxfEZh7NiFbA16GUJpaS0urCL+AU1ZybKlOV6VUleJBWkzCW5rQ/ALNFxI7CVy5w/B3ytkQ6GHi7LSVNcp2EwrfqshtMyAJUGcPU4UqGAVKxuG0FCkPYIGSLakIrg+GJDKh6AHynUFu4MEwURQigEiDIrjgp2x2GTFMvVAUSORxoRFWtLZSy5byTJFlMUM/KjY6G+Q5JF4lL4LKTuEDf2HcukUtTEi8ikPuKztHKLI+UDPqTH2dwaKmKclRPkLi9zehFg8QAFbICBW25QVGAcFbyqpXGKbcsS5JfGMkVpK7xawlMfGqhXp8DgVrSgMhC4CgyugwwYADaq340xs+3YPQFZ2yW2rNkSyzxUUsPHJkWlwstFZUoKWtLxxgxf1bJXDnaLg1ouTVTQETXAquA9arMA/AICFqlOLAJACQDLWHcK1YQKtb4BrVF861K6xtYUrZklKqlUs3rius7XLLQlOikNSOpIBjrTFJYlddOvwCzri1C6pdcTxXWQA113gDdcYp/ZjLO+6AJNdLIJVYBMZ+fH/sHNpWuNaAsgZCCGzqU+LW1kKJBM+FoBdAMA9YRDbF0gmyzoJ8srpaPiVlTdrQSE4xurLlBazxw1vbKcrTBEvpsJc88mrDxOUoawwNgDDcIibGYtMyPAQGSZnsArgrAVgeIDYHCwPx5MEIYyNABsDeZBNfmaAGZWoiszUAkc25KHUhn0b9JKoVpkWK7alCjlagvYMMFXojA0JqGMFL9TljPCG6MmIDQnIOUjALke8YVZssBCdkHJeEpRfzAlUMqoKFLUHnoODBS0VKFwq6m+U4p7AjhkoVEXw3RHryBVwoIQEMEhjSqEROKZEQqx6iXCX0/tHkMimnzzMOQ2AJAAMBii4AmgH0foAEE8BmjTpUM/xHRtwnHzmUbmhjcUECrr1J4d2TpgiAu4ThNB7LXEdFrXnKNhElyvTScoA0Qg+pXiszYVO0lkaZOUIYRLJOkEFCxEUG5zF0HCYMF2tTFVyXe0OzzbGQNG64ZiolCUS01LPHFe2Ex4GbaINOLlWNBQRYU4I8U9yYziSoso4hPyrmJFENJKbYR3wYIvAPrgs5FtWuAmPTCt6y5GV1chmgFpfYLjy5FI4koFDyjtBBEkHPbQEEOzHa/kSmiqeOCe11AAtJmpHITT3Y8KKMp8oOQHH0VRL3Z/grbQOGDrS1HNzc7AjZ1/VIE2UNASHSkkR3zzjq+w9mGb0ViWbxwEWwCuIDYW46JBAoqgEQozachRRlq1rBAIVFQCZM9MVDaxpd7CI6FpvL0BRp2UdzZdlUhUhKrO7tMvIlm1lZijeAYdXUp+ZqQH2FYESbRoDD1ZKxUXeqFO3OKAERJ2p/JB135BwPxsE3CaIQom8TZJuk3xBZN8mv6UBIQ167kNqG9DXrrPW5bf4LWZrYZ0oDZRLwXMs6T3FqW8zk9/MtjUeAeoFKM9NANpSWRgnD5ulVZXpURtVkkahl5jRYCaIDjV7SlBCGLG1IDg17DquupBAnA40bBLhPGokh1B70voYZVi3ilUonB9gNa3kFIaam22WMMWtA/2P3qqU4iD9o+9QLSgTg4QTRMgPAHSsFWhVDS4uhQQaNF3ThmtF+40VlovLd7aAve8zWfNd4GZ+wkG1TjkkVz7KqanCaglTAtDSYuSWu7LEpj0oHJHa/lJrX6ihXFIxovzc4YTRUqSDPtyK//a5G4W8LoG9wVSUKDbUgHmw/m7YbsmewwCQBh9MWNcCHCNClM1BBECMDG05ADu9gBwKIC+jIAbozmTwRAxR511buNIPLdjsiDSzIANZR/XwAi17IQdMnQ0KVuJ3spOqAOqGFVKJyZYS9wW3vVyqXy3g2QEqonS9tJ0cSLy13KmPqPKEMLBKf8OHXQb4DbRh6ZCsWBfP6F7i1myTMRWFO+CQjkgx1QUkFP/VFsfhIFFwZgPsHK6xsboNfYVvyqJDVQJa16hAzDB4GLyNgsoQjGjqbiS4Q2c2JsnOGrM99WLH/S+glLC0OBXoGwed0VWRGg9KYCgKKE8omhhwJcew27PVjWgopAJB3XlBqPXCyWoAtXQjRyl4E5l0LVfRbSQTrKMptguoJ4BQyYAfAaQ08r01mrzGClZA1qadqmpBHhVsup1a7rkXu7JOnupRd7q9W9S/d8rY0JfsX1t9DNhe1mn+BiCH6Clg+mINSA0BAn3p2DeDZXtT2n7UEixtWDvwuJ781+4JpDXhXpiaY69mG3vNhuYy4bm9+GnpcrL6VcY5uz/G/LLvq3ubdufGgTUJpE1iaJNUmmTRJvk2eSjsDEgOMifu7j7pA7EvgnPtGZywTe6HURQXpnFQwiwZwshfAOFMqNgZRh9aBSCICrJxR8DfLV6Dc0TCs6R89/T21i4XlmRjUw6DFMtVxSbDkQklYtkHDDhD8Eu5/V6HZOaYUjoQZ6gyAdQ6C8o+WaAtOGWopSHQlYfbBsaEJW7OtjIVZm/uMNZaXdsi8TtcYUXuq7j6hn3Y8cyJUMoY60SM9aJuOKLw+Do1RT6odBlaxoiyNCDzI671Kq9x4ygGicqxYb2lOGzpTia+Kt78T7e2boMqBBqGwQmUvk3stJNqa9JjGzLJSYj00mY99J+PYyZZm0RvmdIRcHSs+TLIFT6BXs/PKa2amwUSh6FrrFQ0SqzdkIsEuVr1PNz4RokREY0MGyO6XjH+7qBcHsBCqttkNRug8PwFuGrNF1DLSFrCDZ6XDDyOHfZMHPUmo9tJ2PQybk2zy+zQdehXqJ20TAPsQ8i7TtzxQxrD5iM+5WpMvN47xqaUSaiMe70Za2hVGiJmpLiMRagi0B+aEdrQt3k9zEJXQ2jpLg9yxs9IcgJwq0ZkXogsu9grgDZDwU5BL9Po3vTaDnCW4w+vkFsDWBrnftLwtnH8HKkkDDNhxxIlRfWTYFRxrInPelEeSjhHg8Op/U4dVMl6pzX5+lbQZfPKmCY3h14A2EnDINABFfRkPSzdnciGDogKqaLEBIyZyzKjL0cgKwFhaZumbCHirrIVCg0Vvc/QUETkvcChCfQK8GoEiOUHpA6MbYapKl2khcwIV9bK7CgPo7YDUTFuSMEiu5Q/mSgLwcgD/2fJVaPO8C+DCst/I0VeoQxe4dEVCq2LFF9hTyanx/cKQnBRiEGFoFtyrW/FegF72ZgNWVJxy04b8mYRvrsdsq80QQsFEL1yFlyMKozlIWH05w6u2Y25c8AeX/AbGkyVtX6rtmDBjyM3TEJSoSr1zEW8Ia1MJohW3DFxtM8Po92Zn7RUrR0Wov93E4eh1lkPT0LD1UnI90euk3HoT3NrNpKepDR5crNZ6Zzal9FGdPz1GbpTxe5Czzj7XqUtFzVcuIIlsl59U62gMIB5YemZC2kf4IExoBBMInoTRuaGzTbPX+xcbOsexegBaCLzibXo8m8CfBtSMab344/jDdhOxkSzYJvmx2vpvVnG92Jj4i3v6X6M5bRJ0jcdYo2nXWrkIv8GdK3KsmQr9Ni8v7D/NA3ALo5sGwptRb/acUqlwy0ufJriXCB1p3SzZf640sIh5qBzeIuc3HnUtaqpc0gJXPIXrtXjNFe/rGNrAuhpl2gTrbrodHVQuW00JxYGD0AhgSMf2H8vDUkBAVE21YTeLjWQqEkL7TU6clzCHkSgWpYmH3JOtEAIoWkPwJHaYDR2YV1cJO3ANTsRqo1FGHO5SowO23DSFG5UcFNGse831MKhLQTFZE/nvbVR8MDNY6NS90pRug8gPch7pQf2Y2kgFwY8M9UbxYAU407ErCV3BAytNFQMZkyCcSAk619KMd73FQug2+3QX3Mave3QzhNfe9XYnB12q6yx/u5YoZZGooep9qEqpLXt3hu7klpeb22hZRBaB09quk0JSrNyyA6O1bHAe6H7awg0DjI8teFXHGkFtQxeagBfvl1aa9vbe7efVjUMF93URq0nRXV6VT759vSnDvofVV/Yt1bWN4HofazkL6AMaO7mpDzgILlJLfWkPvqG9u5BEye40Yof/MchsaNY1CXoOeBFYSpKUG1rmXnZXjlD2/aFf7njggtFOpTFL3Yo00Gh/A+gFYZJ17T0J5sLKzAanuCBEtPFnS4UY5D4LCFi14hbNzWtKmvTW1lGDrf2t9BBl21ZW52aGPUa1b9vQAbmFuDdQfoFQ7SC0GjvPkEwNCktpUcYNIwlSEZ1qa6o6mLYupEfHqfJyTN+raVbfcJZuz/DNDQgx1JctBqwYfHCYXxn42iv+Oa50bZTiGUOu9zW15y1TrMLU4YIgnGnKjZp/zKuz9Zz7bTgYi6KDUVPenVTwAwM5+ZDOGnUpjQGM8YcrrpnHT9/uU90WCFcoiz9fV5JWf1PE4IzjZwdVRk/HWHvgKZ4QhiDtPiz+/ME5c8luJsyy9ZnRvfwJMd6ACBPXUPpmRvcnXLseCve86W2SUXE3HOmNlM3jtynzxlo4OCD3rQvQwZvVJH3NsZH7DnU8EJvfrvNh3AF3KGQ6iy21GosXKcHFwUoThcdOC+5zB83LUvaPGr0A8o+ZfYQFoGQg5dignFqkAFzHRe34LQmETnLIAN2wIqYRpg87LVYdn89OeNDldXgf4IV38M0AtoeXwXV2SsRRdrG1QvgZO3DppeHV7ECD2x5z2QcBGbOf3OB8JwcBCOKAVkz2WzBu4/Ag9gIeV/5qwXlblNeKMbKq9vDPbgwwr2tHQnumavuE2r9iprm+Y44LYZp5Wn6mgRQx9kpobbYpJs6eGwZ8hjwI90wdskacXjlGH8A9DIJkmcxvcn0FzkOpwwj4F07lJcG+aTykKVviSEI6qSneV2JbuvhTNI0bOmEnFiUVHHztrUP0uXRaNE4urozbqzqV7vjMPHin/UguCmcuNRnjUGZ2M1mfes5m/dzQblYsjn5/dDGRZsvSLeKXJlu+RuaCFTB4qZVzZ8asANbPwAN7Pno3FNnicI1K6Blne9SzhZki/77eZJlrbt0Qvv6nAns/W8bLvekgIVlK+9Vri2xSFIp8L0dpLHHYuTjCea6FjGYpD/RuVoZ216O/mHda9kYYuyf7H6aIzKmJmW6jGtBejYdDFtjtmyrS1ZRInSoqQ1+S0LF0U1qTM6V8w2GAwWjxpq7ve3blwDELoFqZNecdfOvMsdHrO9t1mnLgGPGDtnZbdU3cfCOlYaeenOk8taWjEqsSaqao/0aaPkAGbKwnDoxP4wDE/FhI8cfKGbTUtQBIduiBmjHrOTmd3k/KRxnupGnT6+7Em0QfShw0rh03DIAQe9PB8nm1wyvdGzzYMH7gA+8tlPubZPatRL05Ye3h6Pp8v2Eulcg/srEOnoYWZ+4mVMTVSq5O1J6MNJ6vxCXm9ybNdopeLZlK9Ly+8y/ZfFP94lTxyATjaLCvVcRBjx90+1euHRnuAeV4qCVf6vYJxr9B9NmtfH3z7mpXCbg0XvEEhsjtat4+cdKvnMt3E42am6OMyB+Ay5VzuWEme6X+obBe7mH3+xz7CH/jCrmQ+8nCTrZm/DT0x1g8+SOOVkVmH1eQiww4IBoblDd5JasPId0523lzDsvYBb1QaTi16w/Q8wdyEmMJ2yfTuN3uH/J/O4C8fXczX10GXxLb5Ab0+135GPvUjyPe71BAOL5e+2/Hrdv/42pWpE4ijGrehEJvVPVYDsBxIqdMbvhuivyQqALEJSOxFUgGAOf5EdQPJhWCIB5MTBLYGVtoDyYdq9QFSOz6wiQBTctAcoOsFoCZADfLUWYIb5LAZA5gJARRMkDQAkAsMaAZqOkCUQMBdEzUMmlr/UhQBZfuAeX1XSV+7QjQdAIijkn0BAA=== -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=etherspot/arka&utm_content=198):

> ‼️ **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](https://docs.coderabbit.ai/finishing-touches/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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: cloudflare-workers-and-pages[bot]
created_at: 2025-04-02T03:16:43Z
body: ## Deploying arka with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>3d71167</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://5b2a5093.arka-3qg.pages.dev'>https://5b2a5093.arka-3qg.pages.dev</a>
</td></tr>
<tr><td><strong>Branch Preview URL:</strong></td><td>
<a href='https://pro-3097-arka-epv08.arka-3qg.pages.dev'>https://pro-3097-arka-epv08.arka-3qg.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/180f46b97b38861d0b5eee789435c45a/pages/view/arka/5b2a5093-a618-4d9e-bded-edb77ece09ab)

author_association: NONE