Skip to content

Commit eed38aa

Browse files
authored
fixed exchange rate for chainlink non stable tokens (#143)
* fixed exchange rate for chainlink non stable tokens * updated package version
1 parent 5953d42 commit eed38aa

File tree

4 files changed

+77
-11
lines changed

4 files changed

+77
-11
lines changed

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "arka",
3-
"version": "1.6.2",
3+
"version": "1.6.3",
44
"description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software",
55
"type": "module",
66
"directories": {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Chainlink Native tokens/USD addresses
2+
export const NativeOracles: Record<number, string> = {
3+
1: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", // Ethereum Mainnet
4+
10: "0x13e3Ee699D1909E989722E753853AE30b17e08c5", // Optimism
5+
56: "0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE", // BNB
6+
97: "0x2514895c72f50D8bd4B4F9b1110F0D6bD2c97526", // BNB Testnet
7+
100: "0x22441d81416430A54336aB28765abd31a792Ad37", // Gnosis
8+
137: "0xAB594600376Ec9fD91F8e885dADF0CE036862dE0", // Polygon Mainnet
9+
8453: "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70", // Base Mainnet
10+
80002: "0x001382149eBa3441043c1c66972b4772963f5D43", // Polygon Amoy
11+
84532: "0x4aDC67696bA383F43DD60A9e78F2C97Fbbfc7cb1", // Base Sepolia
12+
43113: "0x5498BB86BC934c8D34FDA08E81D444153d0D06aD", // Avalanche Testnet
13+
43114: "0x0A77230d17318075983913bC2145DB16C7366156", // Avalanche Mainnet
14+
42161: "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612", // Arbitrum Mainnet
15+
421614: "0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165", // Arbitrum Sepolia
16+
59144: "0x3c6Cd9Cc7c7a4c2Cf5a82734CD249D7D593354dA", // Linea Mainnet
17+
534352: "0x6bF14CB0A831078629D993FDeBcB182b21A8774C", // Scroll Mainnet
18+
534351: "0x59F1ec1f10bD7eD9B938431086bC1D9e233ECf41", // Scroll Sepolia
19+
20+
// Yet to Support on Arka
21+
44787: "0x022F9dCC73C5Fb43F2b4eF2EF9ad3eDD1D853946", // Celo Alfajores testnet
22+
42220: "0x0568fD19986748cEfF3301e55c0eb1E729E0Ab7e", // Celo Mainnet
23+
1088: "0xD4a5Bb03B5D66d9bf81507379302Ac2C2DFDFa6D", // Metis Mainnet
24+
1284: "0x4497B606be93e773bbA5eaCFCb2ac5E2214220Eb", // Moonbeam Mainnet
25+
1285: "0x3f8BFbDc1e79777511c00Ad8591cef888C2113C1", // Moonriver Mainnet
26+
4002: "0xe04676B9A9A2973BCb0D1478b5E1E9098BBB7f3D", // Fantom Testnet
27+
250: "0xf4766552D15AE4d256Ad41B6cf2933482B0680dc", // Fantom
28+
324: "0x6D41d1dc818112880b40e26BD6FD347E41008eDA", // zkSync Mainnet
29+
300: "0xfEefF7c3fB57d18C5C6Cdd71e45D2D0b4F9377bF", // zkSync Testnet
30+
1101: "0x97d9F9A00dEE0004BE8ca0A8fa374d486567eE2D", // Polygon zkEVM
31+
2442: "0xd94522a6feF7779f672f4C88eb672da9222f2eAc", // Polygon zkEVM Cardona Testnet
32+
33+
}

backend/src/paymaster/index.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ export class Paymaster {
208208
return paymasterAndData;
209209
}
210210

211-
async getQuotesMultiToken(userOp: any, entryPoint: string, chainId: number, multiTokenPaymasters: any, tokens_list: string[], oracles: any, bundlerRpc: string, oracleName: string, log?: FastifyBaseLogger) {
211+
async getQuotesMultiToken(userOp: any, entryPoint: string, chainId: number, multiTokenPaymasters: any, tokens_list: string[], oracles: any,
212+
bundlerRpc: string, oracleName: string, nativeOracleAddress: string, log?: FastifyBaseLogger) {
212213
try {
213214
const provider = new providers.JsonRpcProvider(bundlerRpc);
214215
const quotes = [], unsupportedTokens = [];
@@ -240,6 +241,14 @@ export class Paymaster {
240241
const paymasterContract = new ethers.Contract(result.paymasterAddress , MultiTokenPaymasterAbi, provider);
241242
result.postOpGas = await paymasterContract.UNACCOUNTED_COST;
242243

244+
let ETHUSDPrice: any, ETHUSDPriceDecimal;
245+
if (oracleName === "chainlink") {
246+
const nativeOracleContract = new ethers.Contract(nativeOracleAddress, ChainlinkOracleAbi, provider);
247+
const ETHprice = await nativeOracleContract.latestRoundData();
248+
ETHUSDPrice = ETHprice.answer;
249+
ETHUSDPriceDecimal = await nativeOracleContract.decimals();
250+
result.etherUSDExchangeRate = ETHprice.answer;
251+
}
243252
for (let i = 0; i < tokens_list.length; i++) {
244253
const gasToken = tokens_list[i];
245254
if (!(multiTokenPaymasters[chainId] && multiTokenPaymasters[chainId][gasToken]) &&
@@ -258,20 +267,28 @@ export class Paymaster {
258267
ethPrice = Number(ethers.utils.formatUnits(ETHprice, 18 - decimals)).toFixed(0);
259268
} else if (oracleName === "chainlink") {
260269
const chainlinkContract = new ethers.Contract(oracleAddress, ChainlinkOracleAbi, provider);
261-
const ETHprice = await chainlinkContract.latestRoundData();
262-
ethPrice = ETHprice.answer;
270+
const ETHpriceDecimal = await chainlinkContract.decimals();
271+
let ETHprice = await chainlinkContract.latestAnswer();
272+
ETHUSDPrice = ethers.utils.formatUnits(ETHUSDPrice, ETHUSDPriceDecimal);
273+
ETHprice = ethers.utils.formatUnits(ETHprice, ETHpriceDecimal);
274+
ETHUSDPrice = ethers.utils.parseEther(ETHUSDPrice);
275+
ETHprice = ethers.utils.parseEther(ETHprice);
276+
const tokenContract = new ethers.Contract(gasToken, ERC20Abi, provider);
277+
const decimals = Number(await tokenContract.decimals());
278+
ethPrice = ethers.utils.parseUnits((ETHUSDPrice/ETHprice).toFixed(decimals), decimals).toString()
263279
} else {
264280
const ecContract = new ethers.Contract(oracleAddress, EtherspotChainlinkOracleAbi, provider);
265281
const ETHprice = await ecContract.cachedPrice();
266282
ethPrice = ETHprice
267283
}
268-
result.etherUSDExchangeRate = BigNumber.from(ethPrice).toHexString();
284+
if (result.etherUSDExchangeRate === "0x")
285+
result.etherUSDExchangeRate = BigNumber.from(ethPrice).toHexString();
269286
const symbol = await tokenContract.symbol();
270287
quotes.push({
271288
token: gasToken,
272289
symbol: symbol,
273290
decimals: decimals,
274-
etherTokenExchangeRate: ethPrice,
291+
etherTokenExchangeRate: BigNumber.from(ethPrice).toHexString(),
275292
serviceFeePercent: (this.multiTokenMarkUp/10000 - 100)
276293
})
277294
}
@@ -288,11 +305,12 @@ export class Paymaster {
288305
}
289306

290307
async signMultiTokenPaymaster(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
291-
feeToken: string, oracleAggregator: string, bundlerRpc: string, signer: Wallet, oracleName: string, log?: FastifyBaseLogger) {
308+
feeToken: string, oracleAggregator: string, bundlerRpc: string, signer: Wallet, oracleName: string, nativeOracleAddress: string, log?: FastifyBaseLogger) {
292309
try {
293310
const provider = new providers.JsonRpcProvider(bundlerRpc);
294311
const paymasterContract = new ethers.Contract(paymasterAddress, MultiTokenPaymasterAbi, provider);
295312
let ethPrice = "";
313+
296314
if (oracleName === "orochi") {
297315
const oracleContract = new ethers.Contract(oracleAggregator, OrochiOracleAbi, provider);
298316
const ETHprice = await oracleContract.getLatestData(1, ethers.utils.hexlify(ethers.utils.toUtf8Bytes('ETH')).padEnd(42, '0'))
@@ -302,9 +320,19 @@ export class Paymaster {
302320
if (decimals < 18)
303321
ethPrice = Number(ethers.utils.formatUnits(ETHprice, 18 - decimals)).toFixed(0);
304322
} else if (oracleName === "chainlink") {
323+
const nativeOracleContract = new ethers.Contract(nativeOracleAddress, ChainlinkOracleAbi, provider);
324+
let ETHUSDPrice = await nativeOracleContract.latestAnswer();
305325
const chainlinkContract = new ethers.Contract(oracleAggregator, ChainlinkOracleAbi, provider);
306-
const ETHprice = await chainlinkContract.latestRoundData();
307-
ethPrice = ETHprice.answer;
326+
const ETHUSDPriceDecimal = await nativeOracleContract.decimals();
327+
const ETHpriceDecimal = await chainlinkContract.decimals();
328+
let ETHprice = await chainlinkContract.latestAnswer();
329+
ETHUSDPrice = ethers.utils.formatUnits(ETHUSDPrice, ETHUSDPriceDecimal);
330+
ETHprice = ethers.utils.formatUnits(ETHprice, ETHpriceDecimal);
331+
ETHUSDPrice = ethers.utils.parseEther(ETHUSDPrice);
332+
ETHprice = ethers.utils.parseEther(ETHprice);
333+
const tokenContract = new ethers.Contract(feeToken, ERC20Abi, provider);
334+
const decimals = Number(await tokenContract.decimals());
335+
ethPrice = ethers.utils.parseUnits((ETHUSDPrice/ETHprice).toFixed(decimals), decimals).toString()
308336
} else {
309337
const ecContract = new ethers.Contract(oracleAggregator, EtherspotChainlinkOracleAbi, provider);
310338
const ETHprice = await ecContract.cachedPrice();

backend/src/routes/paymaster-routes.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { decode } from "../utils/crypto.js";
1212
import { printRequest, getNetworkConfig } from "../utils/common.js";
1313
import { SponsorshipPolicy } from "../models/sponsorship-policy.js";
1414
import { DEFAULT_EP_VERSION, EPVersions, getEPVersion } from "../types/sponsorship-policy-dto.js";
15+
import { NativeOracles } from "../constants/ChainlinkOracles.js";
1516

1617
const paymasterRoutes: FastifyPluginAsync = async (server) => {
1718
const paymaster = new Paymaster(server.config.FEE_MARKUP, server.config.MULTI_TOKEN_MARKUP, server.config.EP7_TOKEN_VGL, server.config.EP7_TOKEN_PGL);
@@ -216,7 +217,9 @@ const paymasterRoutes: FastifyPluginAsync = async (server) => {
216217
!(networkConfig.MultiTokenPaymasterOracleUsed == "orochi" || networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" || networkConfig.MultiTokenPaymasterOracleUsed == "etherspotChainlink"))
217218
throw new Error("Oracle is not Defined/Invalid");
218219
if (!multiTokenPaymasters[chainId]) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK })
219-
result = await paymaster.getQuotesMultiToken(userOp, entryPoint, chainId, multiTokenPaymasters, tokens_list, multiTokenOracles, bundlerUrl, networkConfig.MultiTokenPaymasterOracleUsed, server.log);
220+
if (networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" && !NativeOracles[chainId])
221+
throw new Error("Native Oracle address not set for this chainId")
222+
result = await paymaster.getQuotesMultiToken(userOp, entryPoint, chainId, multiTokenPaymasters, tokens_list, multiTokenOracles, bundlerUrl, networkConfig.MultiTokenPaymasterOracleUsed, NativeOracles[chainId], server.log);
220223
}
221224
else {
222225
if (gasToken && ethers.utils.isAddress(gasToken)) gasToken = ethers.utils.getAddress(gasToken)
@@ -334,7 +337,9 @@ const paymasterRoutes: FastifyPluginAsync = async (server) => {
334337
if (!networkConfig.MultiTokenPaymasterOracleUsed ||
335338
!(networkConfig.MultiTokenPaymasterOracleUsed == "orochi" || networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" || networkConfig.MultiTokenPaymasterOracleUsed == "etherspotChainlink"))
336339
throw new Error("Oracle is not Defined/Invalid");
337-
result = await paymaster.signMultiTokenPaymaster(userOp, str, str1, entryPoint, multiTokenPaymasters[chainId][gasToken], gasToken, multiTokenOracles[chainId][gasToken], bundlerUrl, signer, networkConfig.MultiTokenPaymasterOracleUsed, server.log);
340+
if (networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" && !NativeOracles[chainId])
341+
throw new Error("Native Oracle address not set for this chainId")
342+
result = await paymaster.signMultiTokenPaymaster(userOp, str, str1, entryPoint, multiTokenPaymasters[chainId][gasToken], gasToken, multiTokenOracles[chainId][gasToken], bundlerUrl, signer, networkConfig.MultiTokenPaymasterOracleUsed, NativeOracles[chainId], server.log);
338343
break;
339344
}
340345
default: {

0 commit comments

Comments
 (0)