Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Changelog
## [4.2.2] - 2025-12-09
- Default all the apiKey which would be saved hereafter and update the supportedNetworks to null to make the system only use config.json as default
- skips the getDeposit call from cronJob if the network is testnet

## [4.2.0] - 2025-09-19
- Removed unused code in the repository
- Removed the mode 'erc20' on paymaster routes since it used pimlico paymaster(v1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require('dotenv').config();

async function up({ context: queryInterface }) {
await queryInterface.sequelize.query(
`UPDATE ${process.env.DATABASE_SCHEMA_NAME}."api_keys" SET "SUPPORTED_NETWORKS" = NULL`
);
}

module.exports = { up };
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "arka",
"version": "4.2.1",
"version": "4.2.2",
"description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software",
"type": "module",
"directories": {
Expand Down
35 changes: 17 additions & 18 deletions backend/src/paymaster/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
createPublicClient,
createWalletClient,
http,
parseEther,
parseUnits,
formatUnits,
getAddress,
keccak256,
toHex,
concat,
hexToBytes,
bytesToHex,
import {
createPublicClient,
createWalletClient,
http,
parseEther,
parseUnits,
formatUnits,
getAddress,
keccak256,
toHex,
concat,
hexToBytes,
encodeAbiParameters,
Address,
Hex,
Expand Down Expand Up @@ -53,7 +52,7 @@ const nativePriceCacheTtl = parseInt(process.env.NATIVE_PRICE_CACHE_TTL || "6000
interface TokenPriceAndMetadata {
decimals: number;
symbol: string;
ethPrice: any;
ethPrice: string;
gasToken: string
}

Expand All @@ -62,7 +61,7 @@ interface TokenPriceAndMetadataCache {
expiry: number
}

interface NativeCurrencyPricyCache {
interface NativeCurrencyPriceCache {
data: any;
expiry: number;
}
Expand Down Expand Up @@ -97,7 +96,7 @@ export class Paymaster {
MTP_PVGL: string;
MTP_PPGL: string;
priceAndMetadata: Map<string, TokenPriceAndMetadataCache> = new Map();
nativeCurrencyPrice: Map<string, NativeCurrencyPricyCache> = new Map();
nativeCurrencyPrice: Map<string, NativeCurrencyPriceCache> = new Map();
coingeckoPrice: Map<string, CoingeckoPriceCache> = new Map();
coingeckoService: CoingeckoService = new CoingeckoService();
sequelize: Sequelize;
Expand Down Expand Up @@ -680,7 +679,7 @@ export class Paymaster {
const priceAndMetadata: TokenPriceAndMetadata = {
decimals: Number(data[0].value),
symbol: data[1].value as any,
ethPrice: data[2].value,
ethPrice: data[2].value as string,
gasToken
}
this.priceAndMetadata.set(cacheKey, { data: priceAndMetadata, expiry: Date.now() + ttl });
Expand Down Expand Up @@ -920,7 +919,7 @@ export class Paymaster {
} else {
const ecContract = getContract({ address: oracleAggregator as Address, abi: EtherspotChainlinkOracleAbi, client: publicClient });
const ETHprice = await ecContract.read.cachedPrice();
ethPrice = ETHprice
ethPrice = ETHprice as string;
}
if (userOp.factory && userOp.factoryData) userOp.initCode = concat([userOp.factory as Hex, userOp.factoryData ?? '0x'])
if (!userOp.signature) userOp.signature = '0x';
Expand Down
1 change: 1 addition & 0 deletions backend/src/plugins/sequelizePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ declare module "fastify" {
sponsorshipPolicyRepository: SponsorshipPolicyRepository;
whitelistRepository: WhitelistRepository;
contractWhitelistRepository: ContractWhitelistRepository;
coingeckoRepo: CoingeckoTokensRepository;
multiTokenPaymasterRepository: MultiTokenPaymasterRepository;
}
}
Expand Down
6 changes: 3 additions & 3 deletions backend/src/routes/admin-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
apiKey: body.apiKey,
walletAddress: publicAddress,
privateKey: encode(privateKey, server.config.HMAC_SECRET),
supportedNetworks: body.supportedNetworks,
supportedNetworks: null, // By Default all networks given in config.json will be supported
erc20Paymasters: body.erc20Paymasters,
multiTokenPaymasters: body.multiTokenPaymasters ?? null,
multiTokenOracles: body.multiTokenOracles ?? null,
Expand All @@ -192,7 +192,7 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
apiKey: body.apiKey,
walletAddress: publicAddress,
privateKey: encode(privateKey, server.config.HMAC_SECRET),
supportedNetworks: body.supportedNetworks,
supportedNetworks: null, // By Default all networks given in config.json will be supported
erc20Paymasters: body.erc20Paymasters,
multiTokenPaymasters: body.multiTokenPaymasters ?? null,
multiTokenOracles: body.multiTokenOracles ?? null,
Expand Down Expand Up @@ -229,7 +229,7 @@ const adminRoutes: FastifyPluginAsync = async (server) => {
return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.RECORD_NOT_FOUND });

await apiKeyInstance.update({
supportedNetworks: body.supportedNetworks,
supportedNetworks: null, // By Default all networks given in config.json will be supported
erc20Paymasters: body.erc20Paymasters,
transactionLimit: body.transactionLimit ?? 0,
noOfTransactionsInAMonth: body.noOfTransactionsInAMonth ?? 10,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/routes/sponsorship-policy-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ const sponsorshipPolicyRoutes: FastifyPluginAsync = async (server) => {
return reply.code(ReturnCode.BAD_REQUEST).send({ error: ErrorMessage.INVALID_DATA });
}

const result = await server.sponsorshipPolicyRepository.findOneByWalletAddressAndChain(walletAddress, chainId);
const result = await server.sponsorshipPolicyRepository.findOneByWalletAddressAndChain(walletAddress, Number(chainId));
if (!result) {
return reply.code(ReturnCode.NOT_FOUND).send({ error: ErrorMessage.SPONSORSHIP_POLICY_NOT_FOUND });
}
Expand Down
12 changes: 6 additions & 6 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { MultiTokenPaymaster } from './models/multiTokenPaymaster.js';
import { MULTI_TOKEN_ORACLES, MULTI_TOKEN_PAYMASTERS } from './constants/MultiTokenPaymasterCronJob.js';

let server: FastifyInstance;
const defaultThrustholdValue = '0.001'; // in ETH
const defaultThresholdValue = '0.001'; // in ETH
const defaultTokenOracleDecimals = 8; // Standard oracle decimal

const initializeServer = async (): Promise<void> => {
Expand Down Expand Up @@ -314,7 +314,7 @@ const initializeServer = async (): Promise<void> => {
const thresholdValue = network.thresholdValue ?? networkConfig.thresholdValue;
const bundler = network.bundler ?? networkConfig.bundler;
if (network.contracts?.etherspotPaymasterAddress) {
checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? defaultThrustholdValue, Number(network.chainId), server.log);
checkDeposit(network.contracts.etherspotPaymasterAddress, bundler, process.env.WEBHOOK_URL, thresholdValue ?? defaultThresholdValue, Number(network.chainId), server.log);
}
}
}
Expand All @@ -335,7 +335,7 @@ const initializeServer = async (): Promise<void> => {
if (networkConfig) {
const bundler = networkConfig.bundler;
for (const symbol in customPaymasters[chainId]) {
checkDeposit(customPaymasters[chainId][symbol], bundler, process.env.WEBHOOK_URL, networkConfig.thresholdValue ?? defaultThrustholdValue, Number(chainId), server.log)
checkDeposit(customPaymasters[chainId][symbol], bundler, process.env.WEBHOOK_URL, networkConfig.thresholdValue ?? defaultThresholdValue, Number(chainId), server.log)
}
}
}
Expand All @@ -349,7 +349,7 @@ const initializeServer = async (): Promise<void> => {
if (networkConfig) {
const bundler = networkConfig.bundler;
for (const symbol in customPaymastersV2[chainId]) {
checkDeposit(customPaymastersV2[chainId][symbol], bundler, process.env.WEBHOOK_URL, networkConfig.thresholdValue ?? defaultThrustholdValue, Number(chainId), server.log);
checkDeposit(customPaymastersV2[chainId][symbol], bundler, process.env.WEBHOOK_URL, networkConfig.thresholdValue ?? defaultThresholdValue, Number(chainId), server.log);
}
}
}
Expand All @@ -359,7 +359,7 @@ const initializeServer = async (): Promise<void> => {
// checking deposit for epv6 native paymasters from default config.json.
for (const network of SupportedNetworks) {
if (network.contracts?.etherspotPaymasterAddress) {
checkDeposit(network.contracts.etherspotPaymasterAddress, network.bundler, process.env.WEBHOOK_URL, network.thresholdValue ?? defaultThrustholdValue, Number(network.chainId), server.log);
checkDeposit(network.contracts.etherspotPaymasterAddress, network.bundler, process.env.WEBHOOK_URL, network.thresholdValue ?? defaultThresholdValue, Number(network.chainId), server.log);
}
}

Expand All @@ -368,7 +368,7 @@ const initializeServer = async (): Promise<void> => {
result.forEach((record: MultiTokenPaymaster) => {
const networkConfig = getNetworkConfig(record.chainId, '', server.config.EPV_06);
if (networkConfig)
checkDeposit(getAddress(record.paymasterAddress), networkConfig.bundler, process.env.WEBHOOK_URL ?? '', networkConfig.thresholdValue ?? defaultThrustholdValue, record.chainId, server.log);
checkDeposit(getAddress(record.paymasterAddress), networkConfig.bundler, process.env.WEBHOOK_URL ?? '', networkConfig.thresholdValue ?? defaultThresholdValue, record.chainId, server.log);
})
}
} catch (err) {
Expand Down
16 changes: 8 additions & 8 deletions backend/src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
import { FastifyBaseLogger, FastifyRequest } from "fastify";
import { createPublicClient, defineChain, http, parseUnits } from "viem";
import SupportedNetworks from "../../config.json";
import { EtherscanResponse, getEtherscanFeeResponse } from "./interface.js";
import { EtherscanResponse, getEtherscanFeeResponse, NetworkConfig } from "./interface.js";
import * as chains from 'viem/chains'

export function printRequest(methodName: string, request: FastifyRequest, log: FastifyBaseLogger) {
export function printRequest(methodName: string, request: FastifyRequest, log: FastifyBaseLogger): void {
log.info(methodName, "called: ");
log.info(request.query, "query passed: ");
log.info(request.body, "body passed: ");
Expand Down Expand Up @@ -33,20 +33,20 @@ export function getViemChainDef(chainId: number, rpcUrl?: string): chains.Chain
return customChain;
}

export function getNetworkConfig(key: any, supportedNetworks: any, entryPoint?: string[]) {
export function getNetworkConfig(key: any, supportedNetworks: any, entryPoint?: string[]): NetworkConfig | null {
if (supportedNetworks !== '') {
const buffer = Buffer.from(supportedNetworks, 'base64');
const SUPPORTED_NETWORKS = JSON.parse(buffer.toString());
if (entryPoint === undefined || entryPoint === null || entryPoint.length === 0) {
const result = SUPPORTED_NETWORKS.find((chain: any) => chain["chainId"] == key);
if (!result) {
return SupportedNetworks.find((chain) => chain.chainId == key);
return SupportedNetworks.find((chain) => chain.chainId == key) || null;
}
return result;
}
const result = SUPPORTED_NETWORKS.find((chain: any) => { return chain["chainId"] == key && entryPoint.includes(chain["entryPoint"]) });
if (!result) {
return SupportedNetworks.find((chain) => chain.chainId == key && entryPoint.includes(chain.entryPoint));
return SupportedNetworks.find((chain) => chain.chainId == key && entryPoint.includes(chain.entryPoint)) || null;
}
return result
} else {
Expand All @@ -62,16 +62,16 @@ export function getNetworkConfig(key: any, supportedNetworks: any, entryPoint?:
}
}

export function getChainIdsFromDefaultSupportedNetworks() {
export function getChainIdsFromDefaultSupportedNetworks(): number[] {
return SupportedNetworks.map((chain) => chain.chainId);
}

export function decodeSupportedNetworks(supportedNetworksForDecode: string) {
export function decodeSupportedNetworks(supportedNetworksForDecode: string): NetworkConfig[] {
const buffer = Buffer.from(supportedNetworksForDecode, "base64");
return JSON.parse(buffer.toString());
}

export function getChainIdsFromSupportedNetworks(supportedNetworksForDecode: string) {
export function getChainIdsFromSupportedNetworks(supportedNetworksForDecode: string): number[] {
const decodedSupportedNetworks = decodeSupportedNetworks(supportedNetworksForDecode);
if(!decodedSupportedNetworks)
return [];
Expand Down
11 changes: 11 additions & 0 deletions backend/src/utils/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,15 @@ export interface getEtherscanFeeResponse {
maxFeePerGas: bigint;
maxPriorityFeePerGas: bigint;
gasPrice: bigint;
}

export interface NetworkConfig {
chainId: number;
bundler: string;
contracts: {
etherspotPaymasterAddress: string;
};
thresholdValue: string;
MultiTokenPaymasterOracleUsed: string;
entryPoint: string;
}
4 changes: 4 additions & 0 deletions backend/src/utils/monitorTokenPaymaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import EtherspotAbi from "../abi/EtherspotAbi.js";

export async function checkDeposit(paymasterAddress: string, bundlerUrl: string, webhookUrl: string, thresholdValue: string, chainId: number, log: FastifyBaseLogger) {
try {
if (bundlerUrl.includes('testnet')) {
log.info(`Skipping deposit check for testnet on chainId ${chainId} address: ${paymasterAddress} bunderUrl: ${bundlerUrl}`);
return;
}
const publicClient = createPublicClient({ transport: http(bundlerUrl) });
const contract = getContract({ address: paymasterAddress as `0x${string}`, abi: EtherspotAbi, client: publicClient });
const currentDeposit = await contract.read.getDeposit();
Expand Down