From 23104a48554f04aad1c9de68618a8cca47322fbd Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 10 Oct 2024 12:35:37 -0400 Subject: [PATCH] scripts and contract bug fixes --- .env.example | 2 + contracts/mellowpricefeed/CloneFactory.sol | 9 ++- contracts/mellowpricefeed/MellowPriceFeed.sol | 23 +++--- mainnet_chains.json | 16 +++- scripts/createMellowPriceFeeds.ts | 61 ++++++++++++++++ scripts/deployMellowCloneFactory.ts | 38 ++++++++++ .../deployMellowPriceFeedImplementation.ts | 38 ++++++++++ testnet_chains.json | 73 ++++++++++++++----- 8 files changed, 224 insertions(+), 36 deletions(-) create mode 100644 scripts/createMellowPriceFeeds.ts create mode 100644 scripts/deployMellowCloneFactory.ts create mode 100644 scripts/deployMellowPriceFeedImplementation.ts diff --git a/.env.example b/.env.example index 8b19180..eb4d0a1 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,5 @@ ASSET_LIMIT=5 PRICE_FEED_DECIMALS=9 PRICE_FEED_DESCRIPTIONS=["steakLRT", "Re7LRT", "amphrETH", "rstETH"] QUOTED_PRICE_FEEDS=["WETH/ETH", "WETH/USDC"] +MELLOW_PRICE_FEEDS=["amphrETH/wstETH"] +MELLOW_VAULTS=["0x5fD13359Ba15A84B76f7F87568309040176167cd"] diff --git a/contracts/mellowpricefeed/CloneFactory.sol b/contracts/mellowpricefeed/CloneFactory.sol index 76da69e..db13554 100644 --- a/contracts/mellowpricefeed/CloneFactory.sol +++ b/contracts/mellowpricefeed/CloneFactory.sol @@ -22,16 +22,23 @@ contract CloneFactory { /// @notice Create clone of MellowPriceFeed contract and initialize it. /// @dev Clone method returns address of created clone. + /// @param _vault Address of Mellow LRT vault. /// @param _priceFeedDecimals Amount of decimals a PriceFeed is denominiated in. /// @param _priceFeedBase Base asset of PriceFeed, should be set to asset symbol ticker. /// @param _priceFeedQuote Quote asset of PriceFeed, should be set to asset symbol ticker. function createMellowPriceFeed( + address _vault, uint8 _priceFeedDecimals, string calldata _priceFeedBase, string calldata _priceFeedQuote ) external { address mellowPriceFeedCloneAddress = Clones.clone(implementationAddress); - MellowPriceFeed(mellowPriceFeedCloneAddress).initialize(_priceFeedDecimals, _priceFeedBase, _priceFeedQuote); + MellowPriceFeed(mellowPriceFeedCloneAddress).initialize( + _vault, + _priceFeedDecimals, + _priceFeedBase, + _priceFeedQuote + ); MellowPriceFeedCloneAddresses[msg.sender] = mellowPriceFeedCloneAddress; emit MellowPriceFeedCloneCreated(mellowPriceFeedCloneAddress); } diff --git a/contracts/mellowpricefeed/MellowPriceFeed.sol b/contracts/mellowpricefeed/MellowPriceFeed.sol index 3fcba65..2871cd1 100644 --- a/contracts/mellowpricefeed/MellowPriceFeed.sol +++ b/contracts/mellowpricefeed/MellowPriceFeed.sol @@ -15,7 +15,7 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { string private priceFeedQuote; - IVault public immutable vault; + IVault public vault; uint80 constant DEFAULT_ROUND = 1; @@ -25,20 +25,19 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { error GetRoundDataCanBeOnlyCalledWithLatestRound(uint80 requestedRoundId); - error UnsafeUintToIntConversion(uint256 value); - - constructor(address vault_) { - vault = IVault(vault_); - } - /// @notice Initialize clone of this contract. /// @dev This function is used in place of a constructor in proxy contracts. + /// @param _vault Address of Mellow LRT vault. /// @param _priceFeedDecimals Amount of decimals a PriceFeed is denominiated in. /// @param _priceFeedBase Base asset of PriceFeed. /// @param _priceFeedQuote Quote asset of PriceFeed. - function initialize(uint8 _priceFeedDecimals, string calldata _priceFeedBase, string calldata _priceFeedQuote) - external - initializer { + function initialize( + address _vault, + uint8 _priceFeedDecimals, + string calldata _priceFeedBase, + string calldata _priceFeedQuote + ) external initializer { + vault = IVault(_vault); priceFeedDecimals = _priceFeedDecimals; priceFeedBase = _priceFeedBase; priceFeedQuote = _priceFeedQuote; @@ -105,14 +104,12 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { uint80 answeredInRound ) { roundId = latestRound(); - bytes32 baseAssetName = bytes32(bytes(priceFeedBase)); - bytes32 quoteAssetName = bytes32(bytes(priceFeedQuote)); IVault.ProcessWithdrawalsStack memory processWithdrawalsStack = vault.calculateStack(); answer = 0; if (processWithdrawalsStack.totalSupply != 0) { - answer = int256(processWithdrawalsStack.totalSupply) * 1e18 / int256(processWithdrawalsStack.totalSupply); + answer = int256(processWithdrawalsStack.totalValue) * 1e18 / int256(processWithdrawalsStack.totalSupply); } // These values are equal after chainlink’s OCR update diff --git a/mainnet_chains.json b/mainnet_chains.json index 4e9f187..8bf1cac 100644 --- a/mainnet_chains.json +++ b/mainnet_chains.json @@ -10,8 +10,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0xa1aB70C0F3725AcA1D1e85Bd4402Dd2d5F6AFf19", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xd285A4F0Ad1BB6b1Db8cD3dD839E9f423938ef9E", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Optimism", @@ -24,8 +26,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0xfaC9d315b9b558e10eBdb0462aA42577aADe6601", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0x02Ed15B70D4dE1209c3Dd5a75195CB3f3dDB8B07", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Base", @@ -38,8 +42,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x09d43904C8ABd470df1B793df68904A9714558CF", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xfaC9d315b9b558e10eBdb0462aA42577aADe6601", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Ethereum", @@ -52,7 +58,9 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0xde471274F1B684476d341eB131224F389AD4A270", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0x710C8a3c8CB393cA24748849de3585b5C48D4D0c", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" } ] diff --git a/scripts/createMellowPriceFeeds.ts b/scripts/createMellowPriceFeeds.ts new file mode 100644 index 0000000..3e1a05d --- /dev/null +++ b/scripts/createMellowPriceFeeds.ts @@ -0,0 +1,61 @@ +import { Wallet, ethers } from "ethers"; +import CloneFactory from '../artifacts/contracts/mellowpricefeed/CloneFactory.sol/CloneFactory.json'; +import testnet_chains from '../testnet_chains.json'; +import mainnet_chains from '../mainnet_chains.json'; + +async function main() { + const evmChains = JSON.parse(process.env.EVM_CHAINS!); + const mellowPriceFeedDecimals = process.env.PRICE_FEED_DECIMALS as any; + const mellowPriceFeeds = JSON.parse(process.env.MELLOW_PRICE_FEEDS!); + const mellowVaults = JSON.parse(process.env.MELLOW_VAULTS!); + + if (mellowPriceFeeds.length !== mellowVaults.length) { + throw new Error('unequal amount of mellowVaults associated with mellowPriceFeeds'); + } + + const privateKey = process.env.PRIVATE_KEY; + + if (!privateKey) { + throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); + } + + const mainnet = process.env.MAINNET as string + let chains = testnet_chains.map((chain) => ({ ...chain })); + if (mainnet === "TRUE") { + chains = mainnet_chains.map((chain) => ({ ...chain })); + } + + for (const chain of chains) { + if (evmChains.includes(chain.name)) { + const provider = new ethers.JsonRpcProvider(chain.rpc) + const wallet = new Wallet(privateKey, provider); + const balance = await provider.getBalance(wallet.address) + console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); + + const cloneFactoryMellowContract = new ethers.Contract(chain.cloneFactoryMellow, CloneFactory.abi, wallet) + let i = 0 + for (const mellowPriceFeed of mellowPriceFeeds) { + console.log(`Deploying ${mellowPriceFeed} price feed on ${chain.name}`); + try { + const [baseAsset, quoteAsset] = mellowPriceFeed.split('/'); + + console.log("baseAsset", baseAsset) + console.log("quoteAsset", quoteAsset) + const tx = await cloneFactoryMellowContract.createMellowPriceFeed(mellowPriceFeedDecimals, mellowVaults[i], baseAsset, quoteAsset); + console.log(`Transaction sent: ${tx.hash}`); + + const receipt = await tx.wait(); + console.log(`Transaction mined: ${receipt.transactionHash}`); + } catch (error) { + console.error(`Failed to deploy ${mellowPriceFeed} on ${chain.name}:`, error); + } + i += 1 + } + } + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/deployMellowCloneFactory.ts b/scripts/deployMellowCloneFactory.ts new file mode 100644 index 0000000..8be6397 --- /dev/null +++ b/scripts/deployMellowCloneFactory.ts @@ -0,0 +1,38 @@ +import { Wallet, ethers } from "ethers"; +import CloneFactoryQuoted from '../artifacts/contracts/mellowpricefeed/CloneFactory.sol/CloneFactory.json'; +import testnet_chains from '../testnet_chains.json'; +import mainnet_chains from '../mainnet_chains.json'; + +async function main () { + const evmChains = JSON.parse(process.env.EVM_CHAINS!); + + const privateKey = process.env.PRIVATE_KEY; + + if (!privateKey) { + throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); + } + + const mainnet = process.env.MAINNET as string + let chains = testnet_chains.map((chain) => ({ ...chain })); + if (mainnet === "TRUE") { + chains = mainnet_chains.map((chain) => ({ ...chain })); + } + + for (const chain of chains) { + if (evmChains.includes(chain.name)) { + const provider = new ethers.JsonRpcProvider(chain.rpc) + const wallet = new Wallet(privateKey, provider); + const balance = await provider.getBalance(wallet.address) + console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); + + const cloneFactoryQuotedFactory = new ethers.ContractFactory(CloneFactoryQuoted.abi, CloneFactoryQuoted.bytecode, wallet) + const cloneFactoryQuoted = await cloneFactoryQuotedFactory.deploy(chain.mellowPriceFeedImplementation) + console.log(`${chain.name}, address: ${await cloneFactoryQuoted.getAddress()}`); + } + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/deployMellowPriceFeedImplementation.ts b/scripts/deployMellowPriceFeedImplementation.ts new file mode 100644 index 0000000..9661333 --- /dev/null +++ b/scripts/deployMellowPriceFeedImplementation.ts @@ -0,0 +1,38 @@ +import { Wallet, ethers } from "ethers"; +import MellowPriceFeed from '../artifacts/contracts/mellowpricefeed/MellowPriceFeed.sol/MellowPriceFeed.json'; +import testnet_chains from '../testnet_chains.json'; +import mainnet_chains from '../mainnet_chains.json'; + +async function main() { + const evmChains = JSON.parse(process.env.EVM_CHAINS!); + + const privateKey = process.env.PRIVATE_KEY; + + if (!privateKey) { + throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); + } + + const mainnet = process.env.MAINNET as string + let chains = testnet_chains.map((chain) => ({ ...chain })); + if (mainnet === "TRUE") { + chains = mainnet_chains.map((chain) => ({ ...chain })); + } + + for (const chain of chains) { + if (evmChains.includes(chain.name)) { + const provider = new ethers.JsonRpcProvider(chain.rpc) + const wallet = new Wallet(privateKey, provider); + const balance = await provider.getBalance(wallet.address) + console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); + + const mellowPriceFeedFactory = new ethers.ContractFactory(MellowPriceFeed.abi, MellowPriceFeed.bytecode, wallet) + const mellowPriceFeed = await mellowPriceFeedFactory.deploy() + console.log(`${chain.name}, address: ${await mellowPriceFeed.getAddress()}`); + } + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/testnet_chains.json b/testnet_chains.json index 9e5c8bf..d57f5c5 100644 --- a/testnet_chains.json +++ b/testnet_chains.json @@ -10,8 +10,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Ethereum Sepolia", @@ -24,8 +26,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "0x1A069010D7F572c97925E83a1298Df8f96893c60", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "0x694723e8Fe9945CffDB671b02175DC55DeDf7F29" + "cloneFactoryQuoted": "0x694723e8Fe9945CffDB671b02175DC55DeDf7F29", + "cloneFactoryMellow": "" }, { "name": "BNB Chain", @@ -38,8 +42,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Polygon Mumbai", @@ -52,8 +58,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Polygon zkEVM", @@ -66,8 +74,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Avalanche Fuji C-Chain", @@ -80,8 +90,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Fantom", @@ -94,8 +106,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Moonbase", @@ -108,8 +122,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Arbitrum Goerli", @@ -122,6 +138,7 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", "cloneFactoryQuoted": "" }, @@ -136,8 +153,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x3DB6DF9EDfDcfE97D574Aa6f106C767051561Be2", "priceFeedQuotedImplementation": "0x2Babd8D4BCE072e78aA288c639Ef4516fCe26d89", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xab2c7cc090A45836fae04501e0454413ECA96611", - "cloneFactoryQuoted": "0x4f5E3B2d64670cd8EA2329c4B028a4c07832F846" + "cloneFactoryQuoted": "0x4f5E3B2d64670cd8EA2329c4B028a4c07832F846", + "cloneFactoryMellow": "" }, { "name": "Optimism Goerli", @@ -150,8 +169,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Optimism Sepolia", @@ -164,8 +185,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x48B10B538B7E5af4CbFd93B1C4d36668e8F6F644", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xe9c4145FCeDdc19bc9B788C5d16cF08AD70d3850", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Base Goerli", @@ -178,8 +201,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Base Sepolia", @@ -192,8 +217,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x09d43904C8ABd470df1B793df68904A9714558CF", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0x02Ed15B70D4dE1209c3Dd5a75195CB3f3dDB8B07", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Mantle", @@ -206,8 +233,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Alfajores", @@ -220,8 +249,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Kava", @@ -234,8 +265,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Filecoin Calibration", @@ -248,8 +281,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Linea Goerli", @@ -262,7 +297,9 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" } ]