Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed exchange rate for chainlink non stable tokens #143

Merged
merged 2 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
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
Loading