diff --git a/README.md b/README.md index 181d15b..a5539b9 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,13 @@ There is an option to run the code locally without using AWS and only using loca "LINK": "0x0a6Aa1Bd30D6954cA525315287AdeeEcbb6eFB59" } } which also needs to be converted into `base64` value +- FEE_MARKUP - this is used to add fee if it gets from the provider. This needs to be inputted as a number in terms of gwei +- ETHERSCAN_GAS_ORACLES - the list of urls for all chains. Note that the response got is in terms of etherscan API Documentation https://docs.polygonscan.com/api-endpoints/gas-tracker#get-gas-oracle +The structure should be as follows +{ + "137": "https://api.polygonscan.com/api?module=gastracker&action=gasoracle&apikey=YourApiKeyToken", // Note that you need to replace YourApiKeyToken to actual API key from etherscan + "1": "https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey=YourApiKeyToken" +} which then needs to be converted into `base64` value ## API KEY VALIDATION - In ARKA Admin Frontend, create an API_KEY with the following format - diff --git a/admin_frontend/package-lock.json b/admin_frontend/package-lock.json index 1cac3a6..6be0e5a 100644 --- a/admin_frontend/package-lock.json +++ b/admin_frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "admin_frontend", - "version": "1.1.6", + "version": "1.1.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "admin_frontend", - "version": "1.1.6", + "version": "1.1.7", "dependencies": { "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", diff --git a/admin_frontend/package.json b/admin_frontend/package.json index 39b1cf9..10af6e9 100644 --- a/admin_frontend/package.json +++ b/admin_frontend/package.json @@ -1,6 +1,6 @@ { "name": "admin_frontend", - "version": "1.1.6", + "version": "1.1.7", "private": true, "dependencies": { "@emotion/react": "11.11.3", diff --git a/backend/demo.env b/backend/demo.env index 9e99b8b..69b8af6 100644 --- a/backend/demo.env +++ b/backend/demo.env @@ -9,3 +9,5 @@ STACKUP_API_KEY= SUPPORTED_NETWORKS= ADMIN_WALLET_ADDRESS= DEFAULT_INDEXER_ENDPOINT=http://localhost:3003 +FEE_MARKUP= +ETHERSCAN_GAS_ORACLES= diff --git a/backend/package.json b/backend/package.json index 1873640..adddf33 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "arka", - "version": "1.1.6", + "version": "1.1.7", "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/paymaster/index.test.ts b/backend/src/paymaster/index.test.ts index 9236ac7..5b26a9a 100644 --- a/backend/src/paymaster/index.test.ts +++ b/backend/src/paymaster/index.test.ts @@ -113,7 +113,7 @@ describe('Paymaster on Mumbai', () => { test('SMOKE: validate the deposit function with valid details', async () => { const amount = '0.0000001' try { - const depositResponse = await paymaster.deposit(amount, paymasterAddress, bundlerUrl, relayerKey); + const depositResponse = await paymaster.deposit(amount, paymasterAddress, bundlerUrl, relayerKey, chainId); const expectedMessage = depositResponse.message; const actualMessage = 'Successfully deposited with transaction Hash'; @@ -212,7 +212,7 @@ describe('Paymaster on Mumbai', () => { const wallet = ethers.Wallet.createRandom(); const address = [wallet.address]; // not whitelisted address try { - const whitelistAddresses = await paymaster.whitelistAddresses(address, paymasterAddress, bundlerUrl, relayerKey); + const whitelistAddresses = await paymaster.whitelistAddresses(address, paymasterAddress, bundlerUrl, relayerKey, chainId); if (whitelistAddresses.message.includes('Successfully whitelisted with transaction Hash')) { console.log('The address is whitelisted successfully.') @@ -227,7 +227,7 @@ describe('Paymaster on Mumbai', () => { test('REGRESSION: validate the whitelistAddresses function with already whitelisted address', async () => { const address = ['0x7b3078b9A28DF76453CDfD2bA5E75f32f0676321']; // already whitelisted address try { - await paymaster.whitelistAddresses(address, paymasterAddress, bundlerUrl, relayerKey); + await paymaster.whitelistAddresses(address, paymasterAddress, bundlerUrl, relayerKey, chainId); fail('Address is whitelisted, However it was already whitelisted.') } catch (e: any) { const actualMessage = 'already whitelisted'; @@ -244,7 +244,7 @@ describe('Paymaster on Mumbai', () => { const address = ['0x7b3078b9A28DF76453CDfD2bA5E75f32f0676321']; // already whitelisted address const relayerKey = '0xdd45837c9d94e7cc3ed3b24be7c1951eff6ed3c6fd0baf68fc1ba8c0e51debb'; // invalid relayerKey try { - await paymaster.whitelistAddresses(address, paymasterAddress, bundlerUrl, relayerKey); + await paymaster.whitelistAddresses(address, paymasterAddress, bundlerUrl, relayerKey, chainId); fail('Address is whitelisted, however the relayerKey is invalid.') } catch (e: any) { const actualMessage = 'Please try again later or contact support team RawErrorMsg: hex data is odd-length'; @@ -306,7 +306,7 @@ describe('Paymaster on Mumbai', () => { test('REGRESSION: validate the deposit function with invalid amount', async () => { const amount = '10000' // invalid amount try { - await paymaster.deposit(amount, paymasterAddress, bundlerUrl, relayerKey); + await paymaster.deposit(amount, paymasterAddress, bundlerUrl, relayerKey, chainId); fail('The deposite action is performed with invalid amount.') } catch (e: any) { const actualMessage = 'Balance is less than the amount to be deposited'; diff --git a/backend/src/paymaster/index.ts b/backend/src/paymaster/index.ts index 49e2c92..76dac2c 100644 --- a/backend/src/paymaster/index.ts +++ b/backend/src/paymaster/index.ts @@ -1,10 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { providers, Wallet, ethers, Contract, BigNumber } from 'ethers'; import { arrayify, defaultAbiCoder, hexConcat } from 'ethers/lib/utils.js'; +import { FastifyBaseLogger } from 'fastify'; import abi from "../abi/EtherspotAbi.js"; -import { PimlicoPaymaster, getERC20Paymaster } from './pimlico.js'; +import { PimlicoPaymaster } from './pimlico.js'; import ErrorMessage from '../constants/ErrorMessage.js'; import { PAYMASTER_ADDRESS } from '../constants/Pimlico.js'; +import { getEtherscanFee } from '../utils/common.js'; export class Paymaster { feeMarkUp: BigNumber; @@ -35,7 +37,7 @@ export class Paymaster { return paymasterAndData; } - async sign(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string, bundlerRpc: string, signer: Wallet) { + async sign(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string, bundlerRpc: string, signer: Wallet, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); @@ -57,11 +59,12 @@ export class Paymaster { return returnValue; } catch (err: any) { + if (log) log.error(err, 'sign'); throw new Error('Failed to process request to bundler. Please contact support team RawErrorMsg:' + err.message) } } - async pimlico(userOp: any, bundlerRpc: string, entryPoint: string, PaymasterAddress: string) { + async pimlico(userOp: any, bundlerRpc: string, entryPoint: string, PaymasterAddress: string, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); const erc20Paymaster = new PimlicoPaymaster(PaymasterAddress, provider) @@ -87,10 +90,10 @@ export class Paymaster { const tokenBalance = await tokenContract.balanceOf(userOp.sender); if (tokenAmountRequired.gte(tokenBalance)) throw new Error(`The required token amount ${tokenAmountRequired.toString()} is more than what the sender has ${tokenBalance}`) - + let paymasterAndData = await erc20Paymaster.generatePaymasterAndDataForTokenAmount(userOp, tokenAmountRequired) userOp.paymasterAndData = paymasterAndData; - + const response = await provider.send('eth_estimateUserOperationGas', [userOp, entryPoint]); userOp.verificationGasLimit = ethers.BigNumber.from(response.verificationGasLimit).add(100000).toString(); userOp.preVerificationGas = response.preVerificationGas; @@ -105,21 +108,23 @@ export class Paymaster { }; } catch (err: any) { if (err.message.includes('The required token amount')) throw new Error(err.message); + if (log) log.error(err, 'pimlico'); throw new Error('Failed to process request to bundler. Please contact support team RawErrorMsg: ' + err.message) } } - async pimlicoAddress(gasToken: string, chainId: number) { + async pimlicoAddress(gasToken: string, chainId: number, log?: FastifyBaseLogger) { try { return { message: PAYMASTER_ADDRESS[chainId][gasToken] ?? 'Requested Token Paymaster is not available/deployed', } } catch (err: any) { + if (log) log.error(err, 'pimlicoAddress'); throw new Error(err.message) } } - async whitelistAddresses(address: string[], paymasterAddress: string, bundlerRpc: string, relayerKey: string) { + async whitelistAddresses(address: string[], paymasterAddress: string, bundlerRpc: string, relayerKey: string, chainId: number, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); @@ -131,34 +136,47 @@ export class Paymaster { } } const encodedData = paymasterContract.interface.encodeFunctionData('addBatchToWhitelist', [address]); - const feeData = await provider.getFeeData(); + + const etherscanFeeData = await getEtherscanFee(chainId); + let feeData; + if (etherscanFeeData) { + feeData = etherscanFeeData; + } else { + feeData = await provider.getFeeData(); + feeData.gasPrice = feeData.gasPrice ? feeData.gasPrice.add(this.feeMarkUp) : null; + feeData.maxFeePerGas = feeData.maxFeePerGas ? feeData.maxFeePerGas.add(this.feeMarkUp) : null; + feeData.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ? feeData.maxPriorityFeePerGas.add(this.feeMarkUp) : null; + } + let tx: providers.TransactionResponse; if (!feeData.maxFeePerGas) { tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData, - gasPrice: feeData.gasPrice ? feeData.gasPrice.add(this.feeMarkUp) : undefined, + gasPrice: feeData.gasPrice ?? undefined, }) } else { tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData, - maxFeePerGas: feeData.maxFeePerGas ? feeData.maxFeePerGas.add(this.feeMarkUp) : undefined, - maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ? feeData.maxPriorityFeePerGas.add(this.feeMarkUp) : undefined, + maxFeePerGas: feeData.maxFeePerGas ?? undefined, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, type: 2, }); } await tx.wait(); + return { message: `Successfully whitelisted with transaction Hash ${tx.hash}` }; } catch (err: any) { if (err.message.includes('already whitelisted')) throw new Error(err.message); + if (log) log.error(err, 'whitelistAddresses') throw new Error(ErrorMessage.ERROR_ON_SUBMITTING_TXN + ` RawErrorMsg: ${err.message}`); } } - async removeWhitelistAddress(address: string[], paymasterAddress: string, bundlerRpc: string, relayerKey: string) { + async removeWhitelistAddress(address: string[], paymasterAddress: string, bundlerRpc: string, relayerKey: string, chainId: number, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); @@ -169,81 +187,108 @@ export class Paymaster { throw new Error(`${address[i]} is not whitelisted`) } } + const encodedData = paymasterContract.interface.encodeFunctionData('removeBatchFromWhitelist', [address]); - const feeData = await provider.getFeeData(); + const etherscanFeeData = await getEtherscanFee(chainId); + let feeData; + if (etherscanFeeData) { + feeData = etherscanFeeData; + } else { + feeData = await provider.getFeeData(); + feeData.gasPrice = feeData.gasPrice ? feeData.gasPrice.add(this.feeMarkUp) : null; + feeData.maxFeePerGas = feeData.maxFeePerGas ? feeData.maxFeePerGas.add(this.feeMarkUp) : null; + feeData.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ? feeData.maxPriorityFeePerGas.add(this.feeMarkUp) : null; + } + let tx: providers.TransactionResponse; if (!feeData.maxFeePerGas) { tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData, - gasPrice: feeData.gasPrice ? feeData.gasPrice.add(this.feeMarkUp) : undefined, + gasPrice: feeData.gasPrice ?? undefined, }) } else { tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData, - maxFeePerGas: feeData.maxFeePerGas ? feeData.maxFeePerGas.add(this.feeMarkUp) : undefined, - maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ? feeData.maxPriorityFeePerGas.add(this.feeMarkUp) : undefined, + maxFeePerGas: feeData.maxFeePerGas ?? undefined, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, type: 2, }); } await tx.wait(); + return { message: `Successfully removed whitelisted addresses with transaction Hash ${tx.hash}` }; } catch (err: any) { if (err.message.includes('is not whitelisted')) throw new Error(err.message); + if (log) log.error(err, 'removeWhitelistAddress'); throw new Error(ErrorMessage.ERROR_ON_SUBMITTING_TXN); } } - async checkWhitelistAddress(accountAddress: string, paymasterAddress: string, bundlerRpc: string, relayerKey: string) { + async checkWhitelistAddress(accountAddress: string, paymasterAddress: string, bundlerRpc: string, relayerKey: string, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); const signer = new Wallet(relayerKey, provider) const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); return paymasterContract.check(signer.address, accountAddress); } catch (err) { + if (log) log.error(err, 'checkWhitelistAddress'); throw new Error(ErrorMessage.RPC_ERROR); } } - async deposit(amount: string, paymasterAddress: string, bundlerRpc: string, relayerKey: string) { + async deposit(amount: string, paymasterAddress: string, bundlerRpc: string, relayerKey: string, chainId: number, log?: FastifyBaseLogger) { try { const provider = new providers.JsonRpcProvider(bundlerRpc); const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); const signer = new Wallet(relayerKey, provider) const balance = await signer.getBalance(); - const amountInWei = ethers.utils.parseEther(amount); + const amountInWei = ethers.utils.parseEther(amount.toString()); if (amountInWei.gte(balance)) throw new Error(`${signer.address} Balance is less than the amount to be deposited`) const encodedData = paymasterContract.interface.encodeFunctionData('depositFunds', []); - const feeData = await provider.getFeeData(); + + const etherscanFeeData = await getEtherscanFee(chainId); + let feeData; + if (etherscanFeeData) { + feeData = etherscanFeeData; + } else { + feeData = await provider.getFeeData(); + feeData.gasPrice = feeData.gasPrice ? feeData.gasPrice.add(this.feeMarkUp) : null; + feeData.maxFeePerGas = feeData.maxFeePerGas ? feeData.maxFeePerGas.add(this.feeMarkUp) : null; + feeData.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ? feeData.maxPriorityFeePerGas.add(this.feeMarkUp) : null; + } + let tx: providers.TransactionResponse; if (!feeData.maxFeePerGas) { tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData, value: amountInWei, - gasPrice: feeData.gasPrice ? feeData.gasPrice.add(this.feeMarkUp) : undefined, + gasPrice: feeData.gasPrice ?? undefined, }) } else { tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData, value: amountInWei, - maxFeePerGas: feeData.maxFeePerGas ? feeData.maxFeePerGas.add(this.feeMarkUp) : undefined, - maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ? feeData.maxPriorityFeePerGas.add(this.feeMarkUp) : undefined, + maxFeePerGas: feeData.maxFeePerGas ?? undefined, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ?? undefined, type: 2, }); } // commented the below line to avoid timeouts for long delays in transaction confirmation. // await tx.wait(); + return { message: `Successfully deposited with transaction Hash ${tx.hash}` }; } catch (err: any) { + if (log) log.error(err, 'deposit'); if (err.message.includes('Balance is less than the amount to be deposited')) throw new Error(err.message); throw new Error(ErrorMessage.ERROR_ON_SUBMITTING_TXN); } diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 4847e3f..2d5507d 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -150,14 +150,14 @@ const routes: FastifyPluginAsync = async (server) => { } str += hex; str1 += hex1; - result = await paymaster.sign(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, signer); + result = await paymaster.sign(userOp, str, str1, entryPoint, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, signer, server.log); break; } case 'erc20': { let paymasterAddress: string; if (customPaymasters[chainId] && customPaymasters[chainId][gasToken]) paymasterAddress = customPaymasters[chainId][gasToken]; else paymasterAddress = PAYMASTER_ADDRESS[chainId][gasToken] - result = await paymaster.pimlico(userOp, networkConfig.bundler, entryPoint, paymasterAddress); + result = await paymaster.pimlico(userOp, networkConfig.bundler, entryPoint, paymasterAddress, server.log); break; } case 'default': { @@ -298,7 +298,7 @@ const routes: 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: "Invalid Address passed" }); - const result = await paymaster.whitelistAddresses(address, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey); + const result = await paymaster.whitelistAddresses(address, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey, chainId, server.log); server.log.info(result, 'Response sent: '); if (body.jsonrpc) return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) @@ -356,7 +356,7 @@ const routes: 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: "Invalid Address passed" }); - const result = await paymaster.removeWhitelistAddress(address, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey); + const result = await paymaster.removeWhitelistAddress(address, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey, chainId, server.log); 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); @@ -412,7 +412,7 @@ const routes: FastifyPluginAsync = async (server) => { } const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? ''); if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); - const response = await paymaster.checkWhitelistAddress(accountAddress, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey); + const response = await paymaster.checkWhitelistAddress(accountAddress, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey, server.log); server.log.info(response, 'Response sent: '); if (body.jsonrpc) return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result: { message: response === true ? 'Already added' : 'Not added yet' }, error: null }) @@ -469,7 +469,7 @@ const routes: FastifyPluginAsync = async (server) => { } const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? ''); if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); - return await paymaster.deposit(amount, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey); + return await paymaster.deposit(amount, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey, chainId, server.log); } catch (err: any) { request.log.error(err); if (err.name == "ResourceNotFoundException") diff --git a/backend/src/utils/common.ts b/backend/src/utils/common.ts index 383ff8e..0511230 100644 --- a/backend/src/utils/common.ts +++ b/backend/src/utils/common.ts @@ -1,6 +1,8 @@ import { FastifyBaseLogger, FastifyRequest } from "fastify"; -import SupportedNetworks from "../../config.json" assert { type: "json" }; +import { ethers } from "ethers"; import { Database } from "sqlite3"; +import SupportedNetworks from "../../config.json" assert { type: "json" }; +import { EtherscanResponse, getEtherscanFeeResponse } from "./interface.js"; export function printRequest(methodName: string, request: FastifyRequest, log: FastifyBaseLogger) { log.info(methodName, "called: "); @@ -32,3 +34,37 @@ export async function getSQLdata(apiKey: string, db: Database, log: FastifyBaseL } } +export async function getEtherscanFee(chainId: number, log?: FastifyBaseLogger): Promise { + try { + const etherscanUrlsBase64 = process.env.ETHERSCAN_GAS_ORACLES; + if (etherscanUrlsBase64) { + const buffer = Buffer.from(etherscanUrlsBase64, 'base64'); + const etherscanUrls = JSON.parse(buffer.toString()); + + if (etherscanUrls[chainId]) { + const data = await fetch(etherscanUrls[chainId]); + const response: EtherscanResponse = await data.json(); + + if (response.result && response.result.FastGasPrice) { + const maxFeePerGas = ethers.utils.parseUnits(response.result.suggestBaseFee, 'gwei') + const fastGasPrice = ethers.utils.parseUnits(response.result.FastGasPrice, 'gwei') + return { + maxPriorityFeePerGas: fastGasPrice.sub(maxFeePerGas), + maxFeePerGas, + gasPrice: maxFeePerGas, + } + } + return null; + } + return null; + } + return null; + } catch (err) { + if (log) { + log.error(err); + log.info('fetching from provider'); + } + return null; + } +} + diff --git a/backend/src/utils/interface.ts b/backend/src/utils/interface.ts new file mode 100644 index 0000000..4f8c678 --- /dev/null +++ b/backend/src/utils/interface.ts @@ -0,0 +1,20 @@ +import { BigNumber } from "ethers"; + +export interface EtherscanResponse { + status: string; + message: string; + result?: { + LastBlock: string; + SafeGasPrice: string; + ProposeGasPrice: string; + FastGasPrice: string; + suggestBaseFee: string; + gasUsedRatio: string; + } +} + +export interface getEtherscanFeeResponse { + maxFeePerGas: BigNumber; + maxPriorityFeePerGas: BigNumber; + gasPrice: BigNumber; +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 382614e..107e643 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,8 @@ services: - SUPPORTED_NETWORKS= - CRON_PRIVATE_KEY= - DEFAULT_INDEXER_ENDPOINT=http://localhost:3003 + - FEE_MARKUP= + - ETHERSCAN_GAS_ORACLES= build: context: ./backend dockerfile: Dockerfile diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8a47002..9555622 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "arka_frontend", - "version": "1.1.6", + "version": "1.1.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "arka_frontend", - "version": "1.1.6", + "version": "1.1.7", "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@emotion/react": "^11.11.1", diff --git a/frontend/package.json b/frontend/package.json index d78c700..ee7c363 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "arka_frontend", - "version": "1.1.6", + "version": "1.1.7", "private": true, "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11",