Skip to content

Commit

Permalink
fixed exchange rate for chainlink non stable tokens (#143)
Browse files Browse the repository at this point in the history
* fixed exchange rate for chainlink non stable tokens

* updated package version
  • Loading branch information
vignesha22 authored Oct 3, 2024
1 parent 5953d42 commit eed38aa
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 11 deletions.
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": "1.6.2",
"version": "1.6.3",
"description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software",
"type": "module",
"directories": {
Expand Down
33 changes: 33 additions & 0 deletions backend/src/constants/ChainlinkOracles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Chainlink Native tokens/USD addresses
export const NativeOracles: Record<number, string> = {
1: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", // Ethereum Mainnet
10: "0x13e3Ee699D1909E989722E753853AE30b17e08c5", // Optimism
56: "0x0567F2323251f0Aab15c8dFb1967E4e8A7D42aeE", // BNB
97: "0x2514895c72f50D8bd4B4F9b1110F0D6bD2c97526", // BNB Testnet
100: "0x22441d81416430A54336aB28765abd31a792Ad37", // Gnosis
137: "0xAB594600376Ec9fD91F8e885dADF0CE036862dE0", // Polygon Mainnet
8453: "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70", // Base Mainnet
80002: "0x001382149eBa3441043c1c66972b4772963f5D43", // Polygon Amoy
84532: "0x4aDC67696bA383F43DD60A9e78F2C97Fbbfc7cb1", // Base Sepolia
43113: "0x5498BB86BC934c8D34FDA08E81D444153d0D06aD", // Avalanche Testnet
43114: "0x0A77230d17318075983913bC2145DB16C7366156", // Avalanche Mainnet
42161: "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612", // Arbitrum Mainnet
421614: "0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165", // Arbitrum Sepolia
59144: "0x3c6Cd9Cc7c7a4c2Cf5a82734CD249D7D593354dA", // Linea Mainnet
534352: "0x6bF14CB0A831078629D993FDeBcB182b21A8774C", // Scroll Mainnet
534351: "0x59F1ec1f10bD7eD9B938431086bC1D9e233ECf41", // Scroll Sepolia

// Yet to Support on Arka
44787: "0x022F9dCC73C5Fb43F2b4eF2EF9ad3eDD1D853946", // Celo Alfajores testnet
42220: "0x0568fD19986748cEfF3301e55c0eb1E729E0Ab7e", // Celo Mainnet
1088: "0xD4a5Bb03B5D66d9bf81507379302Ac2C2DFDFa6D", // Metis Mainnet
1284: "0x4497B606be93e773bbA5eaCFCb2ac5E2214220Eb", // Moonbeam Mainnet
1285: "0x3f8BFbDc1e79777511c00Ad8591cef888C2113C1", // Moonriver Mainnet
4002: "0xe04676B9A9A2973BCb0D1478b5E1E9098BBB7f3D", // Fantom Testnet
250: "0xf4766552D15AE4d256Ad41B6cf2933482B0680dc", // Fantom
324: "0x6D41d1dc818112880b40e26BD6FD347E41008eDA", // zkSync Mainnet
300: "0xfEefF7c3fB57d18C5C6Cdd71e45D2D0b4F9377bF", // zkSync Testnet
1101: "0x97d9F9A00dEE0004BE8ca0A8fa374d486567eE2D", // Polygon zkEVM
2442: "0xd94522a6feF7779f672f4C88eb672da9222f2eAc", // Polygon zkEVM Cardona Testnet

}
44 changes: 36 additions & 8 deletions backend/src/paymaster/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ export class Paymaster {
return paymasterAndData;
}

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

let ETHUSDPrice: any, ETHUSDPriceDecimal;
if (oracleName === "chainlink") {
const nativeOracleContract = new ethers.Contract(nativeOracleAddress, ChainlinkOracleAbi, provider);
const ETHprice = await nativeOracleContract.latestRoundData();
ETHUSDPrice = ETHprice.answer;
ETHUSDPriceDecimal = await nativeOracleContract.decimals();
result.etherUSDExchangeRate = ETHprice.answer;
}
for (let i = 0; i < tokens_list.length; i++) {
const gasToken = tokens_list[i];
if (!(multiTokenPaymasters[chainId] && multiTokenPaymasters[chainId][gasToken]) &&
Expand All @@ -258,20 +267,28 @@ export class Paymaster {
ethPrice = Number(ethers.utils.formatUnits(ETHprice, 18 - decimals)).toFixed(0);
} else if (oracleName === "chainlink") {
const chainlinkContract = new ethers.Contract(oracleAddress, ChainlinkOracleAbi, provider);
const ETHprice = await chainlinkContract.latestRoundData();
ethPrice = ETHprice.answer;
const ETHpriceDecimal = await chainlinkContract.decimals();
let ETHprice = await chainlinkContract.latestAnswer();
ETHUSDPrice = ethers.utils.formatUnits(ETHUSDPrice, ETHUSDPriceDecimal);
ETHprice = ethers.utils.formatUnits(ETHprice, ETHpriceDecimal);
ETHUSDPrice = ethers.utils.parseEther(ETHUSDPrice);
ETHprice = ethers.utils.parseEther(ETHprice);
const tokenContract = new ethers.Contract(gasToken, ERC20Abi, provider);
const decimals = Number(await tokenContract.decimals());
ethPrice = ethers.utils.parseUnits((ETHUSDPrice/ETHprice).toFixed(decimals), decimals).toString()
} else {
const ecContract = new ethers.Contract(oracleAddress, EtherspotChainlinkOracleAbi, provider);
const ETHprice = await ecContract.cachedPrice();
ethPrice = ETHprice
}
result.etherUSDExchangeRate = BigNumber.from(ethPrice).toHexString();
if (result.etherUSDExchangeRate === "0x")
result.etherUSDExchangeRate = BigNumber.from(ethPrice).toHexString();
const symbol = await tokenContract.symbol();
quotes.push({
token: gasToken,
symbol: symbol,
decimals: decimals,
etherTokenExchangeRate: ethPrice,
etherTokenExchangeRate: BigNumber.from(ethPrice).toHexString(),
serviceFeePercent: (this.multiTokenMarkUp/10000 - 100)
})
}
Expand All @@ -288,11 +305,12 @@ export class Paymaster {
}

async signMultiTokenPaymaster(userOp: any, validUntil: string, validAfter: string, entryPoint: string, paymasterAddress: string,
feeToken: string, oracleAggregator: string, bundlerRpc: string, signer: Wallet, oracleName: string, log?: FastifyBaseLogger) {
feeToken: string, oracleAggregator: string, bundlerRpc: string, signer: Wallet, oracleName: string, nativeOracleAddress: string, log?: FastifyBaseLogger) {
try {
const provider = new providers.JsonRpcProvider(bundlerRpc);
const paymasterContract = new ethers.Contract(paymasterAddress, MultiTokenPaymasterAbi, provider);
let ethPrice = "";

if (oracleName === "orochi") {
const oracleContract = new ethers.Contract(oracleAggregator, OrochiOracleAbi, provider);
const ETHprice = await oracleContract.getLatestData(1, ethers.utils.hexlify(ethers.utils.toUtf8Bytes('ETH')).padEnd(42, '0'))
Expand All @@ -302,9 +320,19 @@ export class Paymaster {
if (decimals < 18)
ethPrice = Number(ethers.utils.formatUnits(ETHprice, 18 - decimals)).toFixed(0);
} else if (oracleName === "chainlink") {
const nativeOracleContract = new ethers.Contract(nativeOracleAddress, ChainlinkOracleAbi, provider);
let ETHUSDPrice = await nativeOracleContract.latestAnswer();
const chainlinkContract = new ethers.Contract(oracleAggregator, ChainlinkOracleAbi, provider);
const ETHprice = await chainlinkContract.latestRoundData();
ethPrice = ETHprice.answer;
const ETHUSDPriceDecimal = await nativeOracleContract.decimals();
const ETHpriceDecimal = await chainlinkContract.decimals();
let ETHprice = await chainlinkContract.latestAnswer();
ETHUSDPrice = ethers.utils.formatUnits(ETHUSDPrice, ETHUSDPriceDecimal);
ETHprice = ethers.utils.formatUnits(ETHprice, ETHpriceDecimal);
ETHUSDPrice = ethers.utils.parseEther(ETHUSDPrice);
ETHprice = ethers.utils.parseEther(ETHprice);
const tokenContract = new ethers.Contract(feeToken, ERC20Abi, provider);
const decimals = Number(await tokenContract.decimals());
ethPrice = ethers.utils.parseUnits((ETHUSDPrice/ETHprice).toFixed(decimals), decimals).toString()
} else {
const ecContract = new ethers.Contract(oracleAggregator, EtherspotChainlinkOracleAbi, provider);
const ETHprice = await ecContract.cachedPrice();
Expand Down
9 changes: 7 additions & 2 deletions backend/src/routes/paymaster-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { decode } from "../utils/crypto.js";
import { printRequest, getNetworkConfig } from "../utils/common.js";
import { SponsorshipPolicy } from "../models/sponsorship-policy.js";
import { DEFAULT_EP_VERSION, EPVersions, getEPVersion } from "../types/sponsorship-policy-dto.js";
import { NativeOracles } from "../constants/ChainlinkOracles.js";

const paymasterRoutes: 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);
Expand Down Expand Up @@ -216,7 +217,9 @@ const paymasterRoutes: FastifyPluginAsync = async (server) => {
!(networkConfig.MultiTokenPaymasterOracleUsed == "orochi" || networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" || networkConfig.MultiTokenPaymasterOracleUsed == "etherspotChainlink"))
throw new Error("Oracle is not Defined/Invalid");
if (!multiTokenPaymasters[chainId]) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK })
result = await paymaster.getQuotesMultiToken(userOp, entryPoint, chainId, multiTokenPaymasters, tokens_list, multiTokenOracles, bundlerUrl, networkConfig.MultiTokenPaymasterOracleUsed, server.log);
if (networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" && !NativeOracles[chainId])
throw new Error("Native Oracle address not set for this chainId")
result = await paymaster.getQuotesMultiToken(userOp, entryPoint, chainId, multiTokenPaymasters, tokens_list, multiTokenOracles, bundlerUrl, networkConfig.MultiTokenPaymasterOracleUsed, NativeOracles[chainId], server.log);
}
else {
if (gasToken && ethers.utils.isAddress(gasToken)) gasToken = ethers.utils.getAddress(gasToken)
Expand Down Expand Up @@ -334,7 +337,9 @@ const paymasterRoutes: FastifyPluginAsync = async (server) => {
if (!networkConfig.MultiTokenPaymasterOracleUsed ||
!(networkConfig.MultiTokenPaymasterOracleUsed == "orochi" || networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" || networkConfig.MultiTokenPaymasterOracleUsed == "etherspotChainlink"))
throw new Error("Oracle is not Defined/Invalid");
result = await paymaster.signMultiTokenPaymaster(userOp, str, str1, entryPoint, multiTokenPaymasters[chainId][gasToken], gasToken, multiTokenOracles[chainId][gasToken], bundlerUrl, signer, networkConfig.MultiTokenPaymasterOracleUsed, server.log);
if (networkConfig.MultiTokenPaymasterOracleUsed == "chainlink" && !NativeOracles[chainId])
throw new Error("Native Oracle address not set for this chainId")
result = await paymaster.signMultiTokenPaymaster(userOp, str, str1, entryPoint, multiTokenPaymasters[chainId][gasToken], gasToken, multiTokenOracles[chainId][gasToken], bundlerUrl, signer, networkConfig.MultiTokenPaymasterOracleUsed, NativeOracles[chainId], server.log);
break;
}
default: {
Expand Down

0 comments on commit eed38aa

Please sign in to comment.