diff --git a/webapp/.gitignore b/webapp/.gitignore index 243f78ed..e0a7fb41 100644 --- a/webapp/.gitignore +++ b/webapp/.gitignore @@ -22,3 +22,4 @@ npm-debug.log* yarn-debug.log* yarn-error.log* /lib +.vercel diff --git a/webapp/api/_onchain.ts b/webapp/api/_onchain.ts new file mode 100644 index 00000000..fc342aee --- /dev/null +++ b/webapp/api/_onchain.ts @@ -0,0 +1,181 @@ +import { ethers } from "ethers"; +import axios from "axios"; +import ORACLE_ABI from "./abis/oracle.json"; +import wstETH_ABI from "./abis/wstETH.json"; +import rETH_ABI from "./abis/rETH.json"; +import sAVAX_ABI from "./abis/sAVAX.json"; + +const CHAIN_IDS: { + [chainName: string]: number; +} = { + ETH_MAINNET: 1, + AVAX_MAINNET: 43114, +}; + +const ORACLES: { + [chainId: number]: string; +} = { + 1: "0x789cD7AB3742e23Ce0952F6Bc3Eb3A73A0E08833", + 777: "0xb5711dAeC960c9487d95bA327c570a7cCE4982c0", + 43114: "0x108abfBa5AD61bd61A930BFe73394558d60f0b10", +}; + +const ASSETS: { + [chainId: number]: { [asset: string]: string }; +} = { + 1: { + USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + WBTC: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + WETH: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + wstETH: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + rETH: "0xae78736Cd615f374D3085123A210448E74Fc6393", + AAVE: "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + UNI: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + SPELL: "0x090185f2135308BaD17527004364eBcC2D37e5F6", + BADGER: "0x3472A5A71965499acd81997a54BBA8D852C6E53d", + BAL: "0xba100000625a3754423978a60c9317c58a424e3D", + PERP: "0xbC396689893D065F41bc2C6EcbeE5e0085233447", + }, + 777: { + sAMB: "0x683aae5cD37AC94943D05C19E9109D5876113562", + }, + 43114: { + WAVAX: "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7", + sAVAX: "0x2b2C81e08f1Af8835a78Bb2A90AE924ACE0eA4bE", + }, +}; + +const PROVIDERS: { [chainId: number]: ethers.providers.StaticJsonRpcProvider } = + { + 1: new ethers.providers.StaticJsonRpcProvider( + process.env.REACT_APP_MAINNET_URI + ), + 43114: new ethers.providers.StaticJsonRpcProvider( + process.env.REACT_APP_AVAX_URI + ), + 777: new ethers.providers.StaticJsonRpcProvider( + process.env.REACT_APP_MAINNET_URI + ), + }; + +/** + * 1. Get contract settlement state + * 2. Use the pyth price for the listed majors + */ + +type OracleResponse = [ethers.BigNumber, boolean]; + +export interface PriceResponse { + asset: string; + expiryPrice: string; + finalized: boolean; +} + +async function getExpiryPricesForChainId( + chainid: number, + expiryTimestamp: number +) { + let prices: PriceResponse[] = []; + const provider = PROVIDERS[chainid]; + const oracle = new ethers.Contract(ORACLES[chainid], ORACLE_ABI, provider); + const assets = ASSETS[chainid]; + + let promises: Promise[] = []; + + const assetKeys = Object.keys(assets); + + for (const asset of assetKeys) { + const assetAddress = assets[asset]; + const promise = oracle.getExpiryPrice(assetAddress, expiryTimestamp); + promises.push(promise); + } + const responses = await Promise.all(promises); + + for (let i = 0; i < assetKeys.length; i++) { + const asset = assetKeys[i]; + const [expiryPrice, finalized] = responses[i]; + prices.push({ + asset, + finalized, + expiryPrice: ethers.utils.formatUnits(expiryPrice, 8), + }); + } + + return prices; +} +export async function getExpiryPrices( + expiryTimestamp: number +): Promise { + const chainIds = Object.keys(PROVIDERS).map(Number); // Parse the chain IDs as numbers + + const unmergedPromises = chainIds.map((chainId) => + getExpiryPricesForChainId(chainId, expiryTimestamp) + ); + + const unmerged = await Promise.all(unmergedPromises); + const prices = unmerged.flat(); + + return prices; +} + +export async function getwstETHRatio(blockNumber: number): Promise { + const provider = PROVIDERS[CHAIN_IDS["ETH_MAINNET"]]; + const contract = new ethers.Contract( + ASSETS[CHAIN_IDS["ETH_MAINNET"]]["wstETH"], + wstETH_ABI, + provider + ); + const result: string = await contract.callStatic.stEthPerToken({ + blockTag: blockNumber, + }); + return parseFloat(ethers.utils.formatEther(result)); +} + +export async function getrETHRatio(blockNumber: number): Promise { + const provider = PROVIDERS[CHAIN_IDS["ETH_MAINNET"]]; + const contract = new ethers.Contract( + ASSETS[CHAIN_IDS["ETH_MAINNET"]]["rETH"], + rETH_ABI, + provider + ); + const result: string = await contract.callStatic.getExchangeRate({ + blockTag: blockNumber, + }); + return parseFloat(ethers.utils.formatEther(result)); +} + +export async function getsAVAXRatio(blockNumber: number): Promise { + const provider = PROVIDERS[CHAIN_IDS["AVAX_MAINNET"]]; + const contract = new ethers.Contract( + ASSETS[CHAIN_IDS["AVAX_MAINNET"]]["sAVAX"], + sAVAX_ABI, + provider + ); + const result: string = await contract.callStatic.getPooledAvaxByShares( + ethers.utils.parseEther("1"), + { + blockTag: blockNumber, + } + ); + return parseFloat(ethers.utils.formatEther(result)); +} + +export async function getClosestBlock( + timestamp: number, + chainName: string +): Promise { + const url = `https://coins.llama.fi/block/${chainName}/${timestamp.toString()}`; + try { + const response = await axios.get(url); + + const { data } = response; + if (data.timestamp >= timestamp) { + return data.height; + } else { + return data.height + 1; + } + } catch (error) { + console.error(error); + throw error; + } +} diff --git a/webapp/api/_utils.ts b/webapp/api/_utils.ts new file mode 100644 index 00000000..c1e7a800 --- /dev/null +++ b/webapp/api/_utils.ts @@ -0,0 +1,60 @@ +import moment, { Moment } from "moment"; +import { NextApiRequest } from "next"; + +export const parseExpiryFromRequest = (request: NextApiRequest) => { + const ex = request.query["expiry"] as string; + let expiryTimestamp: number; + if (ex === "latest") { + expiryTimestamp = getLastFriday(); + } else { + expiryTimestamp = parseInt(request.query["expiry"] as string); + } + return expiryTimestamp; +}; + +export const getLastFriday = () => { + const now = moment().utc(); + let friday; + + switch (now.isoWeekday()) { + case 1: + case 2: + case 3: + case 4: + friday = moment().utc().day(5).subtract(1, "week"); + break; + case 5: + // On Fridays, if before 8am UTC return last week. If after, return today + if (now.hours() < 8) { + friday = moment().utc().day(5).subtract(1, "week"); + } else { + friday = moment().utc().day(5); + } + break; + case 6: + case 7: + friday = moment().utc().day(5); + break; + } + return (friday as Moment).hours(8).minutes(0).seconds(0).unix(); +}; + +export const getNextFridayTimestamp = () => { + const now = moment().utc(); + let friday; + + switch (now.isoWeekday()) { + case 1: + case 2: + case 3: + friday = moment().utc().day(5); + break; + case 4: + case 5: + case 6: + case 7: + friday = moment().utc().day(5).add(1, "week"); + break; + } + return (friday as Moment).hours(8).minutes(0).seconds(0).unix(); +}; diff --git a/webapp/api/abis/oracle.json b/webapp/api/abis/oracle.json new file mode 100644 index 00000000..360861b7 --- /dev/null +++ b/webapp/api/abis/oracle.json @@ -0,0 +1,399 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newDisputer", + "type": "address" + } + ], + "name": "DisputerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "disputedPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newPrice", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "disputeTimestamp", + "type": "uint256" + } + ], + "name": "ExpiryPriceDisputed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "expiryTimestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "onchainTimestamp", + "type": "uint256" + } + ], + "name": "ExpiryPriceUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pricer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "disputePeriod", + "type": "uint256" + } + ], + "name": "PricerDisputePeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "pricer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lockingPeriod", + "type": "uint256" + } + ], + "name": "PricerLockingPeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "pricer", + "type": "address" + } + ], + "name": "PricerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + } + ], + "name": "StablePriceUpdated", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { + "internalType": "uint256", + "name": "_expiryTimestamp", + "type": "uint256" + }, + { "internalType": "uint256", "name": "_price", "type": "uint256" } + ], + "name": "disputeExpiryPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "endMigration", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { "internalType": "uint80", "name": "_roundId", "type": "uint80" } + ], + "name": "getChainlinkRoundData", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDisputer", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { + "internalType": "uint256", + "name": "_expiryTimestamp", + "type": "uint256" + } + ], + "name": "getExpiryPrice", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "bool", "name": "", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" } + ], + "name": "getPrice", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" } + ], + "name": "getPricer", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_pricer", "type": "address" } + ], + "name": "getPricerDisputePeriod", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_pricer", "type": "address" } + ], + "name": "getPricerLockingPeriod", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { + "internalType": "uint256", + "name": "_expiryTimestamp", + "type": "uint256" + } + ], + "name": "isDisputePeriodOver", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { + "internalType": "uint256", + "name": "_expiryTimestamp", + "type": "uint256" + } + ], + "name": "isLockingPeriodOver", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { "internalType": "uint256[]", "name": "_expiries", "type": "uint256[]" }, + { "internalType": "uint256[]", "name": "_prices", "type": "uint256[]" } + ], + "name": "migrateOracle", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { "internalType": "address", "name": "_pricer", "type": "address" } + ], + "name": "setAssetPricer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_pricer", "type": "address" }, + { "internalType": "uint256", "name": "_disputePeriod", "type": "uint256" } + ], + "name": "setDisputePeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_disputer", "type": "address" } + ], + "name": "setDisputer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { + "internalType": "uint256", + "name": "_expiryTimestamp", + "type": "uint256" + }, + { "internalType": "uint256", "name": "_price", "type": "uint256" } + ], + "name": "setExpiryPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_pricer", "type": "address" }, + { "internalType": "uint256", "name": "_lockingPeriod", "type": "uint256" } + ], + "name": "setLockingPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_asset", "type": "address" }, + { "internalType": "uint256", "name": "_price", "type": "uint256" } + ], + "name": "setStablePrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/webapp/api/abis/rETH.json b/webapp/api/abis/rETH.json new file mode 100644 index 00000000..c2094f2c --- /dev/null +++ b/webapp/api/abis/rETH.json @@ -0,0 +1,332 @@ +[ + { + "inputs": [ + { + "internalType": "contract RocketStorageInterface", + "name": "_rocketStorageAddress", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "time", + "type": "uint256" + } + ], + "name": "EtherDeposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "time", + "type": "uint256" + } + ], + "name": "TokensBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "time", + "type": "uint256" + } + ], + "name": "TokensMinted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_rethAmount", "type": "uint256" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "depositExcess", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "depositExcessCollateral", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getCollateralRate", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_rethAmount", "type": "uint256" } + ], + "name": "getEthValue", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getExchangeRate", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_ethAmount", "type": "uint256" } + ], + "name": "getRethValue", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalCollateral", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_ethAmount", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/webapp/api/abis/sAVAX.json b/webapp/api/abis/sAVAX.json new file mode 100644 index 00000000..b08dd600 --- /dev/null +++ b/webapp/api/abis/sAVAX.json @@ -0,0 +1,937 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "AccrueRewards", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldCooldownPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newCooldownPeriod", + "type": "uint256" + } + ], + "name": "CooldownPeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "MintingPaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "MintingResumed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockRequestedAt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shareAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "avaxAmount", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shareAmount", + "type": "uint256" + } + ], + "name": "RedeemOverdueShares", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldRedeemPeriod", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newRedeemPeriod", + "type": "uint256" + } + ], + "name": "RedeemPeriodUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "avaxAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shareAmount", + "type": "uint256" + } + ], + "name": "Submitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldTotalPooldAvaxCap", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newTotalPooledAvaxCap", + "type": "uint256" + } + ], + "name": "TotalPooledAvaxCapUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockRequestedAt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shareAmount", + "type": "uint256" + } + ], + "name": "UnlockCancelled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shareAmount", + "type": "uint256" + } + ], + "name": "UnlockRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_ACCRUE_REWARDS", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_DEPOSIT", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_PAUSE", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_PAUSE_MINTING", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_RESUME", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_RESUME_MINTING", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_SET_TOTAL_POOLED_AVAX_CAP", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ROLE_WITHDRAW", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "accrueRewards", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cancelPendingUnlockRequests", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cancelRedeemableUnlockRequests", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "unlockIndex", "type": "uint256" } + ], + "name": "cancelUnlockRequest", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "cooldownPeriod", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "uint256", "name": "from", "type": "uint256" }, + { "internalType": "uint256", "name": "to", "type": "uint256" } + ], + "name": "getPaginatedUnlockRequests", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "startedAt", "type": "uint256" }, + { + "internalType": "uint256", + "name": "shareAmount", + "type": "uint256" + } + ], + "internalType": "struct StakedAvaxStorage.UnlockRequest[]", + "name": "", + "type": "tuple[]" + }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "shareAmount", "type": "uint256" } + ], + "name": "getPooledAvaxByShares", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleAdmin", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "getRoleMember", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" } + ], + "name": "getRoleMemberCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "avaxAmount", "type": "uint256" } + ], + "name": "getSharesByPooledAvax", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" } + ], + "name": "getUnlockRequestCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "hasRole", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "historicalExchangeRateTimestamps", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "historicalExchangeRatesByTimestamp", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_cooldownPeriod", + "type": "uint256" + }, + { "internalType": "uint256", "name": "_redeemPeriod", "type": "uint256" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "mintingPaused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseMinting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "redeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "unlockIndex", "type": "uint256" } + ], + "name": "redeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "redeemOverdueShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "unlockIndex", "type": "uint256" } + ], + "name": "redeemOverdueShares", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "redeemPeriod", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "shareAmount", "type": "uint256" } + ], + "name": "requestUnlock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resume", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resumeMinting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes32", "name": "role", "type": "bytes32" }, + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newCooldownPeriod", + "type": "uint256" + } + ], + "name": "setCooldownPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newRedeemPeriod", + "type": "uint256" + } + ], + "name": "setRedeemPeriod", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newTotalPooledAvaxCap", + "type": "uint256" + } + ], + "name": "setTotalPooledAvaxCap", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stakerCount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "submit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "totalPooledAvax", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalPooledAvaxCap", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalShares", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "userSharesInCustody", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "", "type": "address" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "name": "userUnlockRequests", + "outputs": [ + { "internalType": "uint256", "name": "startedAt", "type": "uint256" }, + { "internalType": "uint256", "name": "shareAmount", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/webapp/api/abis/wstETH.json b/webapp/api/abis/wstETH.json new file mode 100644 index 00000000..fede4889 --- /dev/null +++ b/webapp/api/abis/wstETH.json @@ -0,0 +1,252 @@ +[ + { + "inputs": [ + { "internalType": "contract IStETH", "name": "_stETH", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { + "internalType": "uint256", + "name": "subtractedValue", + "type": "uint256" + } + ], + "name": "decreaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_wstETHAmount", "type": "uint256" } + ], + "name": "getStETHByWstETH", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_stETHAmount", "type": "uint256" } + ], + "name": "getWstETHByStETH", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "nonces", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stETH", + "outputs": [ + { "internalType": "contract IStETH", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stEthPerToken", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tokensPerStEth", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "address", "name": "recipient", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_wstETHAmount", "type": "uint256" } + ], + "name": "unwrap", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_stETHAmount", "type": "uint256" } + ], + "name": "wrap", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/webapp/api/constants.ts b/webapp/api/constants.ts new file mode 100644 index 00000000..9d0af09e --- /dev/null +++ b/webapp/api/constants.ts @@ -0,0 +1,31 @@ +// Can get from: https://pyth.network/developers/price-feed-ids +export const PYTH_ASSET_TO_PRICE_FEED_ID: Record = { + ETH: "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", + BTC: "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", + SOL: "ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d", + BNB: "2f95862b045670cd22bee3114c39763a4a08beeb663b145d283c31d7d1101c4f", + MATIC: "5de33a9112c2b700b8d30b8a3402c103578ccfa2765696471cc672bd5cf6ac52", + DOGE: "dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c", + LTC: "6e3f3fa8253588df9326580180233eb791e03b443a3ba7a1d892e73874e19a54", + USDC: "eaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a", + LDO: "c63e2a7f37a04e5e614c07238bedb25dcc38927fba8fe890597a593c0b2fa4ad", + OP: "385f64d993f7b77d8182ed5003d97c60aa3361f3cecfe711544d2d59165e9bdf", + ARB: "3fa4252848f9f0a1480be62745a4629d9eb1322aebab8a791e344b3b9c1adcf5", + APT: "03ae4db29ed4ae33d323568895aa00337e658e348b37509f5372ae51f0af00d5", + SUI: "23d7315113f5b1d3ba7a83604c44b94d79f4fd69af77f804fc7f920a6dc65744", + PEPE: "d69731a2e74ac1ce884fc3890f7ee324b6deb66147055249568869ed700882e4", + AVAX: "93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7", + AAVE: "2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445", + SPELL: "1dcf38b0206d27849b0fcb8d2df21aff4f95873cce223f49d7c1ea3c5145ec63", + BAL: "07ad7b4a7662d19a6bc675f6b467172d2f3947fa653ca97555a9b20236406628", + PERP: "944f2f908c5166e0732ea5b610599116cd8e1c41f47452697c1e84138b7184d6", + APE: "15add95022ae13563a11992e727c91bdb6b55bc183d9d747436c80a483d8c864", + CRV: "a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae8", + BLUR: "856aac602516addee497edf6f50d39e8c95ae5fb0da1ed434a8c2ab9c3e877e9", +}; + +export const PYTH_PRICE_FEED_ID_TO_ASSET: Record = {}; + +for (const [asset, id] of Object.entries(PYTH_ASSET_TO_PRICE_FEED_ID)) { + PYTH_PRICE_FEED_ID_TO_ASSET[id] = asset; +} diff --git a/webapp/api/formatted-expiries.ts b/webapp/api/formatted-expiries.ts new file mode 100644 index 00000000..c5e66c75 --- /dev/null +++ b/webapp/api/formatted-expiries.ts @@ -0,0 +1,106 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { + getwstETHRatio, + getrETHRatio, + getClosestBlock, + getsAVAXRatio, +} from "./_onchain"; +import { parseExpiryFromRequest } from "./_utils"; +import axios from "axios"; +import { + PYTH_ASSET_TO_PRICE_FEED_ID, + PYTH_PRICE_FEED_ID_TO_ASSET, +} from "./constants"; + +export interface FeedResponse { + publishTime: string; + symbol: string; + price: number; + confidence: number; +} + +async function fetchPythPrices(timestamp: number): Promise { + try { + const queryString = Object.values(PYTH_ASSET_TO_PRICE_FEED_ID) + .map((id) => `ids=${id}`) + .join("&"); + const url = `https://benchmarks.pyth.network/v1/updates/price/${timestamp}?${queryString}`; + const startTime = Date.now(); + const response = await axios.get(url, { + headers: { "Accept-Encoding": "gzip,deflate,compress" }, + }); + const endTime = Date.now(); + + const elapsedTime = endTime - startTime; + console.log(`Time taken for pyth request: ${elapsedTime}ms`); + return response.data.parsed; + } catch (error) { + console.error(error); + throw error; + } +} + +export default async function handler( + request: NextApiRequest, + response: NextApiResponse +) { + const expiryTimestamp = parseExpiryFromRequest(request); + let text = "asset,expiry_price\n"; + const closestBlockNumberEthereum = await getClosestBlock( + expiryTimestamp, + "ethereum" + ); + const closestBlockNumberAvax = await getClosestBlock(expiryTimestamp, "avax"); + + console.log( + `Block used for exchange rates: + Ethereum: ${closestBlockNumberEthereum.toString()} + Avax: ${closestBlockNumberAvax.toString()}` + ); + + const priceRatioPromises = [ + getwstETHRatio(closestBlockNumberEthereum), + getrETHRatio(closestBlockNumberEthereum), + getsAVAXRatio(closestBlockNumberAvax), + ]; + const [wstETHPriceRatio, rETHPriceRatio, sAVAXPriceRatio] = await Promise.all( + priceRatioPromises + ); + + console.log("wsteth ratio: ", wstETHPriceRatio); + console.log("reth ratio: ", rETHPriceRatio); + console.log("savax ratio: ", sAVAXPriceRatio); + + const assetPrices = await fetchPythPrices(expiryTimestamp); + let ethPrice = null; + let avaxPrice = null; + + for (const { + id, + price: { price, expo }, + } of assetPrices) { + const asset = PYTH_PRICE_FEED_ID_TO_ASSET[id]; + const formattedPrice = ( + parseFloat(price) / + 10 ** Math.abs(parseInt(expo)) + ).toFixed(8); + + text += `${asset},${formattedPrice}\n`; + + if (asset === "ETH") ethPrice = formattedPrice; + if (asset === "AVAX") avaxPrice = formattedPrice; + } + + if (ethPrice) { + const wstETHPrice = (parseFloat(ethPrice!) * wstETHPriceRatio).toFixed(8); + const rETHPrice = (parseFloat(ethPrice!) * rETHPriceRatio).toFixed(8); + text += `wstETH,${wstETHPrice}\nrETH,${rETHPrice}\n`; + } + if (avaxPrice) { + const sAVAXPrice = (parseFloat(avaxPrice!) * sAVAXPriceRatio).toFixed(8); + text += `sAVAX,${sAVAXPrice}\n`; + } + response.setHeader("content-type", "text/plain"); + + response.status(200).send(text); +} diff --git a/webapp/api/tsconfig.json b/webapp/api/tsconfig.json new file mode 100644 index 00000000..2bb1b12c --- /dev/null +++ b/webapp/api/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "CommonJS", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + } + \ No newline at end of file