diff --git a/.github/workflows/run-scenarios.yaml b/.github/workflows/run-scenarios.yaml index d67424fac..30fb925d4 100644 --- a/.github/workflows/run-scenarios.yaml +++ b/.github/workflows/run-scenarios.yaml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - bases: [ development, mainnet, mainnet-weth, arbitrum-usdt, goerli, goerli-weth, sepolia-usdc, sepolia-weth, fuji, mumbai, polygon, arbitrum-usdc.e, arbitrum-usdc, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-usdbc, base-weth, base-usdc, base-goerli, base-goerli-weth, linea-goerli, optimism-usdc, scroll-goerli, scroll-usdc] + bases: [ development, mainnet, mainnet-weth, goerli, goerli-weth, sepolia-usdc, sepolia-weth, fuji, mumbai, polygon, arbitrum-usdc.e, arbitrum-usdc, arbitrum-weth, arbitrum-usdt, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-usdbc, base-weth, base-usdc, base-goerli, base-goerli-weth, linea-goerli, optimism-usdc, optimism-usdt, scroll-goerli, scroll-usdc] name: Run scenarios env: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} diff --git a/deployments/arbitrum/weth/configuration.json b/deployments/arbitrum/weth/configuration.json new file mode 100644 index 000000000..aacebfc58 --- /dev/null +++ b/deployments/arbitrum/weth/configuration.json @@ -0,0 +1,54 @@ +{ + "name": "Compound WETH", + "symbol": "cWETHv3", + "baseToken": "WETH", + "baseTokenAddress": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "borrowMin": "0.1e18", + "governor": "0x3fB4d38ea7EC20D91917c09591490Eeda38Cf88A", + "pauseGuardian": "0x78E6317DD6D43DdbDa00Dce32C2CbaFc99361a9d", + "rewardTokenAddress": "0x354A6dA3fcde098F8389cad84b0182725c6C91dE", + "storeFrontPriceFactor": 0.7, + "targetReserves": "5000e18", + "rates": { + "supplyKink": 0.85, + "supplySlopeLow": 0.0185, + "supplySlopeHigh": 1, + "supplyBase": 0, + "borrowKink": 0.85, + "borrowSlopeLow": 0.014, + "borrowSlopeHigh": 1.15, + "borrowBase": 0.01 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "69444444444e0", + "baseBorrowSpeed": "46296296296e0", + "baseMinForRewards": "1000e18" + }, + "assets": { + "weETH": { + "address": "0x35751007a407ca6FEFfE80b3cB397736D2cf4dbe", + "decimals": "18", + "borrowCF": 0.82, + "liquidateCF": 0.87, + "liquidationFactor": 0.92, + "supplyCap": "550e18" + }, + "rETH": { + "address": "0xEC70Dcb4A1EFa46b8F2D97C310C9c4790ba5ffA8", + "decimals": "18", + "borrowCF": 0.90, + "liquidateCF": 0.93, + "liquidationFactor": 0.97, + "supplyCap": "800e18" + }, + "wstETH": { + "address": "0x5979D7b546E38E414F7E9822514be443A4800529", + "decimals": "18", + "borrowCF": 0.88, + "liquidateCF": 0.93, + "liquidationFactor": 0.97, + "supplyCap": "2000e18" + } + } +} \ No newline at end of file diff --git a/deployments/arbitrum/weth/deploy.ts b/deployments/arbitrum/weth/deploy.ts new file mode 100644 index 000000000..661241d30 --- /dev/null +++ b/deployments/arbitrum/weth/deploy.ts @@ -0,0 +1,71 @@ +import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet, exp } from '../../../src/deploy'; + +export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise { + const WETH = await deploymentManager.existing('WETH', '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', 'arbitrum'); + const rETH = await deploymentManager.existing('rETH', '0xEC70Dcb4A1EFa46b8F2D97C310C9c4790ba5ffA8', 'arbitrum'); + const wstETH = await deploymentManager.existing('wstETH', '0x5979D7b546E38E414F7E9822514be443A4800529', 'arbitrum'); + const COMP = await deploymentManager.existing('COMP', '0x354A6dA3fcde098F8389cad84b0182725c6C91dE', 'arbitrum'); + + // Deploy WstETHPriceFeed + const wstETHPriceFeed = await deploymentManager.deploy( + 'wstETH:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0xb523AE262D20A936BC152e6023996e46FDC2A95D', // wstETH / ETH price feed + 8 // decimals + ] + ); + + // Deploy constant price feed for WETH + const wethConstantPriceFeed = await deploymentManager.deploy( + 'WETH:priceFeed', + 'pricefeeds/ConstantPriceFeed.sol', + [ + 8, // decimals + exp(1, 8) // constantPrice + ] + ); + + // Deploy scaling price feed for rETH + const rETHScalingPriceFeed = await deploymentManager.deploy( + 'rETH:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0xD6aB2298946840262FcC278fF31516D39fF611eF', // rETH / ETH price feed + 8 // decimals + ] + ); + + // Deploy scaling price feed for weETH + const weETHScalingPriceFeed = await deploymentManager.deploy( + 'weETH:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0xE141425bc1594b8039De6390db1cDaf4397EA22b', // weETH / ETH price feed + 8 // decimals + ] + ); + + // Import shared contracts from cUSDCv3 + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'arbitrum', 'usdc.e'); + const cometFactory = await deploymentManager.fromDep('cometFactory', 'arbitrum', 'usdc.e'); + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'arbitrum', 'usdc.e'); + const configurator = await deploymentManager.fromDep('configurator', 'arbitrum', 'usdc.e'); + const rewards = await deploymentManager.fromDep('rewards', 'arbitrum', 'usdc.e'); + const bulker = await deploymentManager.fromDep('bulker', 'arbitrum', 'usdc.e'); + const localTimelock = await deploymentManager.fromDep('timelock', 'arbitrum', 'usdc.e'); + const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'arbitrum', 'usdc.e'); + + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + + return { + ...deployed, + bridgeReceiver, + bulker, + rewards, + COMP + }; +} \ No newline at end of file diff --git a/deployments/arbitrum/weth/migrations/1716912328_configure_and_ens.ts b/deployments/arbitrum/weth/migrations/1716912328_configure_and_ens.ts new file mode 100644 index 000000000..57c70b163 --- /dev/null +++ b/deployments/arbitrum/weth/migrations/1716912328_configure_and_ens.ts @@ -0,0 +1,296 @@ +import { Contract, ethers } from 'ethers'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { calldata, exp, getConfigurationStruct, proposal } from '../../../../src/deploy'; +import { expect } from 'chai'; +import { applyL1ToL2Alias, estimateL2Transaction, estimateTokenBridge } from '../../../../scenario/utils/arbitrumUtils'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'; +const ENSRegistryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; + +const WETHAmountToBridge = ethers.BigNumber.from(exp(10, 18)); +const arbitrumCOMPAddress = '0x354A6dA3fcde098F8389cad84b0182725c6C91dE'; + +export default migration('1713517203_configurate_and_ens', { + prepare: async (_deploymentManager: DeploymentManager) => { + return {}; + }, + + enact: async (deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + const { utils } = ethers; + + const cometFactory = await deploymentManager.fromDep('cometFactory', 'arbitrum', 'usdc.e'); + const { + bridgeReceiver, + timelock: l2Timelock, + comet, + cometAdmin, + configurator, + rewards + } = await deploymentManager.getContracts(); + const { + arbitrumInbox, + arbitrumL1GatewayRouter, + timelock, + governor, + WETH, + COMP + } = await govDeploymentManager.getContracts(); + const refundAddress = l2Timelock.address; + const wethGatewayAddress = await arbitrumL1GatewayRouter.getGateway(WETH.address); + + const wethGasParams = await estimateTokenBridge( + { + token: COMP.address, + from: timelock.address, + to: comet.address, + amount: WETHAmountToBridge.toBigInt() + }, + govDeploymentManager, + deploymentManager + ); + + const configuration = await getConfigurationStruct(deploymentManager); + const setFactoryCalldata = await calldata( + configurator.populateTransaction.setFactory(comet.address, cometFactory.address) + ); + const setConfigurationCalldata = await calldata( + configurator.populateTransaction.setConfiguration(comet.address, configuration) + ); + const deployAndUpgradeToCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, comet.address] + ); + + const setRewardConfigCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [comet.address, arbitrumCOMPAddress] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [configurator.address, configurator.address, cometAdmin.address, rewards.address], + [0, 0, 0, 0], + [ + 'setFactory(address,address)', + 'setConfiguration(address,(address,address,address,address,address,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint104,uint104,uint104,(address,address,uint8,uint64,uint64,uint64,uint128)[]))', + 'deployAndUpgradeTo(address,address)', + 'setRewardConfig(address,address)' + ], + [ + setFactoryCalldata, + setConfigurationCalldata, + deployAndUpgradeToCalldata, + setRewardConfigCalldata + ] + ] + ); + + const ENSResolver = await govDeploymentManager.existing('ENSResolver', ENSResolverAddress); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const polygonChainId = (await deploymentManager.hre.ethers.provider.getNetwork()).chainId.toString(); + const newMarketObject = { baseSymbol: 'WETH', cometAddress: comet.address }; + const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey)); + + if (officialMarketsJSON[polygonChainId]) { + officialMarketsJSON[polygonChainId].push(newMarketObject); + } else { + officialMarketsJSON[polygonChainId] = [newMarketObject]; + } + + const createRetryableTicketGasParams = await estimateL2Transaction( + { + from: applyL1ToL2Alias(timelock.address), + to: bridgeReceiver.address, + data: l2ProposalData + }, + deploymentManager + ); + + const outboundTransferCalldata = utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256', 'uint256', 'uint256', 'bytes'], + [ + WETH.address, + comet.address, + WETHAmountToBridge.toBigInt(), + wethGasParams.gasLimit, + wethGasParams.maxFeePerGas, + utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [wethGasParams.maxSubmissionCost, '0x'] + ) + ] + ); + + const mainnetActions = [ + // 1. Set Comet configuration and deployAndUpgradeTo new Comet on Arbitrum. + { + contract: arbitrumInbox, + signature: 'createRetryableTicket(address,uint256,uint256,address,address,uint256,uint256,bytes)', + args: [ + bridgeReceiver.address, // address to, + 0, // uint256 l2CallValue, + createRetryableTicketGasParams.maxSubmissionCost, // uint256 maxSubmissionCost, + refundAddress, // address excessFeeRefundAddress, + refundAddress, // address callValueRefundAddress, + createRetryableTicketGasParams.gasLimit, // uint256 gasLimit, + createRetryableTicketGasParams.maxFeePerGas, // uint256 maxFeePerGas, + l2ProposalData, // bytes calldata data + ], + value: createRetryableTicketGasParams.deposit + }, + // 2. Wrap some ETH as WETH + { + contract: WETH, + signature: 'deposit()', + args: [], + value: WETHAmountToBridge, + }, + // 3. Approve the WETH gateway to take Timelock's WETH for bridging + { + contract: WETH, + signature: 'approve(address,uint256)', + args: [wethGatewayAddress, WETHAmountToBridge] + }, + // 4. Bridge WETH from mainnet to Arbitrum Comet + { + target: arbitrumL1GatewayRouter.address, + signature: 'outboundTransfer(address,address,uint256,uint256,uint256,bytes)', + calldata: outboundTransferCalldata, + value: wethGasParams.deposit + }, + // 5. Update the list of official markets + { + target: ENSResolverAddress, + signature: 'setText(bytes32,string,string)', + calldata: ethers.utils.defaultAbiCoder.encode( + ['bytes32', 'string', 'string'], + [subdomainHash, ENSTextRecordKey, JSON.stringify(officialMarketsJSON)] + ) + } + ]; + + const description = "# Initialize cWETHv3 on Arbitrum\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes deployment of Compound III to the Arbitrum network. This proposal takes the governance steps recommended and necessary to initialize a Compound III WETH market on Arbitrum; upon execution, cWETHv3 will be ready for use. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-eth-market-on-arbitrum/5252/2).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/860), [deploy market GitHub action run](https://github.com/woof-software/comet/actions/runs/9419069237/job/25948008428) and [forum discussion](https://www.comp.xyz/t/add-eth-market-on-arbitrum/5252).\n\nThe market migration to create a new WETH Comet market on Arbitrum also includes a new collateral asset weETH which has not been used in any other Compound market previously. In prior analysis of weETH as a collateral asset, Gauntlet identified oracle risks which [could expose the protocol to exaggerated market movements](https://www.comp.xyz/t/add-weeth-market-on-ethereum/5179/3#oracle-risk-7) and a yield risk which could [cause yield shocks and consequentially elevate slippage magnitude and liquidity on DEXs](https://www.comp.xyz/t/add-weeth-market-on-ethereum/5179/3#yield-risk-8).\n\n\n## Proposal Actions\n\nThe first proposal action sets the Comet configuration and deploys a new Comet implementation on Arbitrum. This sends the encoded `setFactory`, `setConfiguration`, `deployAndUpgradeTo` calls across the bridge to the governance receiver on Arbitrum. It also calls `setRewardConfig` on the Arbitrum rewards contract, to establish Artitrum’s bridged version of COMP as the reward token for the deployment and set the initial supply speed to be 6 COMP/day and borrow speed to be 4 COMP/day.\n\nThe second action wraps ETH as WETH so it can be then transferred.\n\nThe third action approves (ArbitrumL1GatewayRouter) [TokenMessenger](https://etherscan.io/address/0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef) to take the Timelock's WETH on Mainnet, in order to seed the market reserves through the arbitrumL1GatewayRouter.\n\nThe fourth action bridges WETH from mainnet via ‘outboundTransfer’ function on ArbitrumL1GatewayRouter’s contract to mint native WETH to Comet on Arbitrum.\n\nThe fifth action updates the ENS TXT record `v3-official-markets` on `v3-additional-grants.compound-community-licenses.eth`, updating the official markets JSON to include the new Arbitrum cWETHv3 market.\n"; + const txn = await govDeploymentManager.retry(async () => + trace(await governor.propose(...(await proposal(mainnetActions, description)))) + ); + + const event = txn.events.find(event => event.event === 'ProposalCreated'); + const [proposalId] = event.args; + + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(deploymentManager: DeploymentManager): Promise { + return true; + }, + + async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager) { + const ethers = deploymentManager.hre.ethers; + + const { + comet, + rewards, + wstETH, + rETH, + weETH + } = await deploymentManager.getContracts(); + + const { + timelock + } = await govDeploymentManager.getContracts(); + + // 1. + const weETHInfo = await comet.getAssetInfoByAddress(weETH.address); + const rETHInfo = await comet.getAssetInfoByAddress(rETH.address); + const wstETHInfo = await comet.getAssetInfoByAddress(wstETH.address); + expect(weETHInfo.supplyCap).to.be.eq(exp(550, 18)); + expect(wstETHInfo.supplyCap).to.be.eq(exp(2000, 18)); + expect(rETHInfo.supplyCap).to.be.eq(exp(800, 18)); + + expect(await comet.pauseGuardian()).to.be.eq('0x78E6317DD6D43DdbDa00Dce32C2CbaFc99361a9d'); + + // 2. & 3. & 4. + expect(await comet.getReserves()).to.be.equal(WETHAmountToBridge); + + // 5. + const ENSResolver = await govDeploymentManager.existing('ENSResolver', ENSResolverAddress); + const ENSRegistry = await govDeploymentManager.existing('ENSRegistry', ENSRegistryAddress); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const officialMarketsJSON = await ENSResolver.text(subdomainHash, ENSTextRecordKey); + const officialMarkets = JSON.parse(officialMarketsJSON); + expect(await ENSRegistry.recordExists(subdomainHash)).to.be.equal(true); + expect(await ENSRegistry.owner(subdomainHash)).to.be.equal(timelock.address); + expect(await ENSRegistry.resolver(subdomainHash)).to.be.equal(ENSResolverAddress); + expect(await ENSRegistry.ttl(subdomainHash)).to.be.equal(0); + expect(officialMarkets).to.deep.equal({ + 1: [ + { + baseSymbol: 'USDC', + cometAddress: '0xc3d688B66703497DAA19211EEdff47f25384cdc3', + }, + { + baseSymbol: 'WETH', + cometAddress: '0xA17581A9E3356d9A858b789D68B4d866e593aE94', + }, + ], + 137: [ + { + baseSymbol: 'USDC', + cometAddress: '0xF25212E676D1F7F89Cd72fFEe66158f541246445', + }, + ], + 8453: [ + { + baseSymbol: 'USDbC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x46e6b214b524310239732D51387075E0e70970bf', + }, + { + baseSymbol: 'USDC', + cometAddress: '0xb125E6687d4313864e53df431d5425969c15Eb2F', + }, + ], + 42161: [ + { + baseSymbol: 'USDC.e', + cometAddress: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA', + }, + { + baseSymbol: 'USDC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: comet.address, + }, + ], + 534352: [ + { + baseSymbol: 'USDC', + cometAddress: '0xB2f97c1Bd3bf02f5e74d13f02E3e26F93D77CE44', + }, + ], + 10: [ + { + baseSymbol: 'USDC', + cometAddress: '0x2e44e174f7D53F0212823acC11C01A11d58c5bCB', + }, + ], + }); + + // 8. + expect(await comet.baseTrackingSupplySpeed()).to.be.equal(exp(6 / 86400, 15, 18)); + expect(await comet.baseTrackingBorrowSpeed()).to.be.equal(exp(4 / 86400, 15, 18)); + } +}); \ No newline at end of file diff --git a/deployments/arbitrum/weth/relations.ts b/deployments/arbitrum/weth/relations.ts new file mode 100644 index 000000000..b42cc0bf2 --- /dev/null +++ b/deployments/arbitrum/weth/relations.ts @@ -0,0 +1,53 @@ +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + 'governor': { + artifact: 'contracts/bridges/polygon/PolygonBridgeReceiver.sol:PolygonBridgeReceiver', + }, + TransparentUpgradeableProxy: { + artifact: 'contracts/ERC20.sol:ERC20' + }, + OssifiableProxy: { + artifact: 'contracts/ERC20.sol:ERC20' + }, + ClonableBeaconProxy: { + artifact: 'contracts/ERC20.sol:ERC20' + }, + // WETH + '0x82af49447d8a07e3bd95bd0d56f35241523fbab1': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + // rETH + '0xEC70Dcb4A1EFa46b8F2D97C310C9c4790ba5ffA8': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + // wstETH + '0x5979D7b546E38E414F7E9822514be443A4800529': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + // weETH + '0x35751007a407ca6FEFfE80b3cB397736D2cf4dbe': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + } +}; \ No newline at end of file diff --git a/deployments/arbitrum/weth/roots.json b/deployments/arbitrum/weth/roots.json new file mode 100644 index 000000000..b82141555 --- /dev/null +++ b/deployments/arbitrum/weth/roots.json @@ -0,0 +1,8 @@ +{ + "comet": "0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486", + "configurator": "0xb21b06D71c75973babdE35b49fFDAc3F82Ad3775", + "rewards": "0x88730d254A2f7e6AC8388c3198aFd694bA9f7fae", + "bridgeReceiver": "0x42480C37B249e33aABaf4c22B20235656bd38068", + "bulker": "0xbdE8F31D2DdDA895264e27DD990faB3DC87b372d", + "COMP": "0x354A6dA3fcde098F8389cad84b0182725c6C91dE" +} \ No newline at end of file diff --git a/deployments/optimism/usdt/configuration.json b/deployments/optimism/usdt/configuration.json new file mode 100644 index 000000000..a6e5e3665 --- /dev/null +++ b/deployments/optimism/usdt/configuration.json @@ -0,0 +1,57 @@ +{ + "name": "Compound USDT", + "symbol": "cUSDTv3", + "baseToken": "USDT", + "baseTokenAddress": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", + "baseTokenPriceFeed": "0xECef79E109e997bCA29c1c0897ec9d7b03647F5E", + "pauseGuardian": "0x3fFd6c073a4ba24a113B18C8F373569640916A45", + "borrowMin": "1e5", + "storeFrontPriceFactor": 0.6, + "targetReserves": "20000000e6", + "rates": { + "supplyKink": 0.9, + "supplySlopeLow": 0.059, + "supplySlopeHigh": 2.9, + "supplyBase": 0, + "borrowKink": 0.9, + "borrowSlopeLow": 0.061, + "borrowSlopeHigh": 3.2, + "borrowBase": 0.015 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "0e15", + "baseBorrowSpeed": "0e15", + "baseMinForRewards": "1000e6" + }, + "assets": { + "OP": { + "address": "0x4200000000000000000000000000000000000042", + "priceFeed": "0x0D276FC14719f9292D5C1eA2198673d1f4269246", + "decimals": "18", + "borrowCF": 0.65, + "liquidateCF": 0.7, + "liquidationFactor": 0.8, + "supplyCap": "0e18" + }, + "WETH": { + "address": "0x4200000000000000000000000000000000000006", + "priceFeed": "0x13e3Ee699D1909E989722E753853AE30b17e08c5", + "decimals": "18", + "borrowCF": 0.83, + "liquidateCF": 0.9, + "liquidationFactor": 0.95, + "supplyCap": "0e18" + }, + "WBTC": { + "address": "0x68f180fcCe6836688e9084f035309E29Bf0A2095", + "priceFeed": "0x718A5788b89454aAE3A028AE9c111A29Be6c2a6F", + "decimals": "8", + "borrowCF": 0.8, + "liquidateCF": 0.85, + "liquidationFactor": 0.95, + "supplyCap": "0e8" + } + } + } + \ No newline at end of file diff --git a/deployments/optimism/usdt/deploy.ts b/deployments/optimism/usdt/deploy.ts new file mode 100644 index 000000000..ce633038b --- /dev/null +++ b/deployments/optimism/usdt/deploy.ts @@ -0,0 +1,71 @@ +import { + Deployed, + DeploymentManager, +} from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet } from '../../../src/deploy'; + +const HOUR = 60 * 60; +const DAY = 24 * HOUR; + +export default async function deploy( + deploymentManager: DeploymentManager, + deploySpec: DeploySpec +): Promise { + const deployed = await deployContracts(deploymentManager, deploySpec); + return deployed; +} + +async function deployContracts( + deploymentManager: DeploymentManager, + deploySpec: DeploySpec +): Promise { + const trace = deploymentManager.tracer(); + + const USDT = await deploymentManager.existing( + 'USDT', + '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', + 'optimism' + ); + const WETH = await deploymentManager.existing( + 'WETH', + '0x4200000000000000000000000000000000000006', + 'optimism' + ); + const WBTC = await deploymentManager.existing( + 'WBTC', + '0x68f180fcCe6836688e9084f035309E29Bf0A2095', + 'optimism' + ); + const OP = await deploymentManager.existing( + 'OP', + '0x4200000000000000000000000000000000000042', + 'optimism' + ); + + const COMP = await deploymentManager.existing( + 'COMP', + '0x7e7d4467112689329f7E06571eD0E8CbAd4910eE', + 'optimism' + ); + + // Import shared contracts from cUSDCv3 + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'optimism', 'usdc'); + const cometFactory = await deploymentManager.fromDep('cometFactory', 'optimism', 'usdc'); + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'optimism', 'usdc'); + const configurator = await deploymentManager.fromDep('configurator', 'optimism', 'usdc'); + const rewards = await deploymentManager.fromDep('rewards', 'optimism', 'usdc'); + const bulker = await deploymentManager.fromDep('bulker', 'optimism', 'usdc'); + const localTimelock = await deploymentManager.fromDep('timelock', 'optimism', 'usdc'); + const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'optimism', 'usdc'); + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + + return { + ...deployed, + bridgeReceiver, + bulker, + rewards, + COMP + }; +} diff --git a/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts b/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts new file mode 100644 index 000000000..d457597e7 --- /dev/null +++ b/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts @@ -0,0 +1,316 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { Contract, ethers } from 'ethers'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { diffState, getCometConfig } from '../../../../plugins/deployment_manager/DiffState'; +import { + calldata, + exp, + getConfigurationStruct, + proposal, +} from '../../../../src/deploy'; +import { expect } from 'chai'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'; +const ENSRegistryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; +const opCOMPAddress = '0x7e7d4467112689329f7E06571eD0E8CbAd4910eE'; +const USDTAmountToBridge = exp(10_000, 6); +const cUSDTAddress = '0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9'; +const mainnetUSDTAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; + +export default migration('1713012100_configurate_and_ens', { + prepare: async (deploymentManager: DeploymentManager) => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager + ) => { + const trace = deploymentManager.tracer(); + const { utils } = ethers; + + const cometFactory = await deploymentManager.fromDep( + 'cometFactory', + 'optimism', + 'usdc' + ); + const { + bridgeReceiver, + timelock: localTimelock, + comet, + cometAdmin, + configurator, + rewards, + USDT + } = await deploymentManager.getContracts(); + + const { + opL1CrossDomainMessenger, + opL1StandardBridge, + governor, + timelock + } = await govDeploymentManager.getContracts(); + + // ENS Setup + // See also: https://docs.ens.domains/contract-api-reference/name-processing + const ENSResolver = await govDeploymentManager.existing( + 'ENSResolver', + ENSResolverAddress + ); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const opChainId = ( + await deploymentManager.hre.ethers.provider.getNetwork() + ).chainId.toString(); + const newMarketObject = { baseSymbol: 'USDT', cometAddress: comet.address }; + const officialMarketsJSON = JSON.parse( + await ENSResolver.text(subdomainHash, ENSTextRecordKey) + ); + if (officialMarketsJSON[opChainId]) { + officialMarketsJSON[opChainId].push(newMarketObject); + } else { + officialMarketsJSON[opChainId] = [newMarketObject]; + } + + const configuration = await getConfigurationStruct(deploymentManager); + const setFactoryCalldata = await calldata( + configurator.populateTransaction.setFactory( + comet.address, + cometFactory.address + ) + ); + const setConfigurationCalldata = await calldata( + configurator.populateTransaction.setConfiguration( + comet.address, + configuration + ) + ); + const deployAndUpgradeToCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, comet.address] + ); + const setRewardConfigCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [comet.address, opCOMPAddress] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [ + configurator.address, + configurator.address, + cometAdmin.address, + rewards.address, + ], + [0, 0, 0, 0], + [ + 'setFactory(address,address)', + 'setConfiguration(address,(address,address,address,address,address,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint104,uint104,uint104,(address,address,uint8,uint64,uint64,uint64,uint128)[]))', + 'deployAndUpgradeTo(address,address)', + 'setRewardConfig(address,address)', + ], + [ + setFactoryCalldata, + setConfigurationCalldata, + deployAndUpgradeToCalldata, + setRewardConfigCalldata, + ], + ] + ); + + const USDTMainnet = new Contract( + mainnetUSDTAddress, + [ + 'function balanceOf(address account) external view returns (uint256)', + 'function approve(address,uint256) external' + ], + govDeploymentManager.hre.ethers.provider + ); + + const notEnoughUSDT = (await USDTMainnet.balanceOf(timelock.address)).lt(USDTAmountToBridge); + const amountToSupply = notEnoughUSDT ? ethers.BigNumber.from(USDTAmountToBridge).sub(await USDTMainnet.balanceOf(timelock.address)) : 0; + const _reduceReservesCalldata = utils.defaultAbiCoder.encode( + ['uint256'], + [amountToSupply] + ); + + // COMP speeds for rewards are setted, but the bridge of tokens does not happen, because it was bridged in the USDC market proposal + + const actions = [ + // 1. Set Comet configuration + deployAndUpgradeTo new Comet, set Reward Config on Optimism + { + contract: opL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [bridgeReceiver.address, l2ProposalData, 3_000_000], + }, + // 2. Get USDT reserves from cUSDT contract + { + target: cUSDTAddress, + signature: '_reduceReserves(uint256)', + calldata: _reduceReservesCalldata + }, + // 3. Approve USDT to L1StandardBridge + { + contract: USDTMainnet, + signature: 'approve(address,uint256)', + args: [opL1StandardBridge.address, USDTAmountToBridge], + }, + // 4. Bridge USDT from Ethereum to OP Comet using L1StandardBridge + { + contract: opL1StandardBridge, + // function depositERC20To(address _l1Token, address _l2Token, address _to, uint256 _amount, uint32 _l2Gas,bytes calldata _data) + signature: + 'depositERC20To(address,address,address,uint256,uint32,bytes)', + args: [ + USDTMainnet.address, + USDT.address, + comet.address, + USDTAmountToBridge, + 200_000, + '0x', + ], + }, + // 5. Update the list of official markets + { + target: ENSResolverAddress, + signature: 'setText(bytes32,string,string)', + calldata: ethers.utils.defaultAbiCoder.encode( + ['bytes32', 'string', 'string'], + [subdomainHash, ENSTextRecordKey, JSON.stringify(officialMarketsJSON)] + ), + }, + ]; + + // the description has speeds. speeds will be set up on on-chain proposal + const description = "# Initialize cUSDTv3 on Optimism\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes the deployment of Compound III to the Optimism network. This proposal takes the governance steps recommended and necessary to initialize a Compound III USDT market on Optimism; upon execution, cUSDTv3 will be ready for use. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based on the [recommendations from Gauntlet](https://www.comp.xyz/t/deploy-compound-iii-on-optimism/4975/6).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/848) and [forum discussion](https://www.comp.xyz/t/deploy-compound-iii-on-optimism/4975).\n\n\n## Proposal Actions\n\nThe first proposal action sets the Comet configuration and deploys a new Comet implementation on Optimism. This sends the encoded `setFactory`, `setConfiguration` and `deployAndUpgradeTo` calls across the bridge to the governance receiver on Optimism. It also calls `setRewardConfig` on the Optimism rewards contract, to establish Optimism’s bridged version of COMP as the reward token for the deployment and set the initial supply speed to be 5 COMP/day and borrow speed to be 5 COMP/day.\n\nThe second action reduces Compound [cUSDT](https://etherscan.io/address/0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9) reserves to Timelock, in order to seed the market reserves through the Optimism L1StandardBridge.\n\nThe third action approves Optimism’s [L1StandardBridge](https://etherscan.io/address/0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1) to take Timelock's USDT, in order to seed the reserves through the bridge.\n\nThe fourth action deposits 10K USDT from mainnet to the Optimism L1StandardBridge contract to bridge to Comet.\n\nThe fifth action updates the ENS TXT record `v3-official-markets` on `v3-additional-grants.compound-community-licenses.eth`, updating the official markets JSON to include the new Optimism cUSDTv3 market"; + const txn = await govDeploymentManager.retry(async () =>{ + return trace(await governor.propose(...(await proposal(actions, description)))); + } + ); + + const event = txn.events.find((event) => event.event === 'ProposalCreated'); + const [proposalId] = event.args; + + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(deploymentManager: DeploymentManager): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) { + const ethers = deploymentManager.hre.ethers; + + const { + comet, + rewards, + WBTC, + WETH, + OP, + COMP + } = await deploymentManager.getContracts(); + + const { + timelock + } = await govDeploymentManager.getContracts(); + + const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); + + // uncomment on on-chain proposal PR + // expect(stateChanges).to.deep.equal({ + // WBTC: { + // supplyCap: exp(400, 8) + // }, + // WETH: { + // supplyCap: exp(11_000, 18) + // }, + // OP: { + // supplyCap: exp(10_000_000, 18) + // }, + // baseTrackingSupplySpeed: exp(5 / 86400, 15, 18), + // baseTrackingBorrowSpeed: exp(5 / 86400, 15, 18), + // }); + + const config = await rewards.rewardConfig(comet.address); + expect(config.token).to.be.equal(COMP.address); + expect(config.rescaleFactor).to.be.equal(exp(1, 12)); + expect(config.shouldUpscale).to.be.equal(true); + + // 2. & 3 & 4. + expect(await comet.getReserves()).to.be.equal(USDTAmountToBridge); + + // 5. + const ENSResolver = await govDeploymentManager.existing( + "ENSResolver", + ENSResolverAddress + ); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const officialMarketsJSON = await ENSResolver.text( + subdomainHash, + ENSTextRecordKey + ); + const officialMarkets = JSON.parse(officialMarketsJSON); + expect(officialMarkets).to.deep.equal({ + 1: [ + { + baseSymbol: 'USDC', + cometAddress: '0xc3d688B66703497DAA19211EEdff47f25384cdc3', + }, + { + baseSymbol: 'WETH', + cometAddress: '0xA17581A9E3356d9A858b789D68B4d866e593aE94', + }, + ], + 137: [ + { + baseSymbol: 'USDC', + cometAddress: '0xF25212E676D1F7F89Cd72fFEe66158f541246445', + }, + ], + 8453: [ + { + baseSymbol: 'USDbC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x46e6b214b524310239732D51387075E0e70970bf', + }, + { + baseSymbol: 'USDC', + cometAddress: '0xb125E6687d4313864e53df431d5425969c15Eb2F', + }, + ], + 42161: [ + { + baseSymbol: 'USDC.e', + cometAddress: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA', + }, + { + baseSymbol: 'USDC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + ], + 534352: [ + { + baseSymbol: 'USDC', + cometAddress: '0xB2f97c1Bd3bf02f5e74d13f02E3e26F93D77CE44', + }, + ], + 10: [ + { + baseSymbol: 'USDC', + cometAddress: '0x2e44e174f7D53F0212823acC11C01A11d58c5bCB', + }, + { + baseSymbol: 'USDT', + cometAddress: comet.address, + }, + ], + }); + } +}); \ No newline at end of file diff --git a/deployments/optimism/usdt/relations.ts b/deployments/optimism/usdt/relations.ts new file mode 100644 index 000000000..281057307 --- /dev/null +++ b/deployments/optimism/usdt/relations.ts @@ -0,0 +1,30 @@ +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + governor: { + artifact: + 'contracts/bridges/optimism/OptimismBridgeReceiver.sol:OptimismBridgeReceiver', + }, + Proxy: { + artifact: 'contracts/ERC20.sol:ERC20', + }, + + l2CrossDomainMessenger: { + delegates: { + field: { + slot: + '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc', + }, + }, + }, + + l2StandardBridge: { + delegates: { + field: { + slot: + '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc', + }, + }, + }, +}; \ No newline at end of file diff --git a/deployments/optimism/usdt/roots.json b/deployments/optimism/usdt/roots.json new file mode 100644 index 000000000..1b7b5cb67 --- /dev/null +++ b/deployments/optimism/usdt/roots.json @@ -0,0 +1,11 @@ +{ + "l2CrossDomainMessenger": "0x4200000000000000000000000000000000000007", + "l2StandardBridge": "0x4200000000000000000000000000000000000010", + "CCTPMessageTransmitter": "0x4D41f22c5a0e5c74090899E5a8Fb597a8842b3e8", + "comet": "0x995E394b8B2437aC8Ce61Ee0bC610D617962B214", + "configurator": "0x84E93EC6170ED630f5ebD89A1AAE72d4F63f2713", + "rewards": "0x443EA0340cb75a160F31A440722dec7b5bc3C2E9", + "bridgeReceiver": "0xC3a73A70d1577CD5B02da0bA91C0Afc8fA434DAF", + "bulker": "0xcb3643CC8294B23171272845473dEc49739d4Ba3", + "COMP": "0x7e7d4467112689329f7E06571eD0E8CbAd4910eE" +} \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 16f3bd3fc..87059717a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -28,6 +28,7 @@ import mainnetWethRelationConfigMap from './deployments/mainnet/weth/relations'; import polygonRelationConfigMap from './deployments/polygon/usdc/relations'; import arbitrumBridgedUsdcRelationConfigMap from './deployments/arbitrum/usdc.e/relations'; import arbitrumNativeUsdcRelationConfigMap from './deployments/arbitrum/usdc/relations'; +import arbitrumWETHRelationConfigMap from './deployments/arbitrum/weth/relations'; import arbitrumBridgedUsdcGoerliRelationConfigMap from './deployments/arbitrum-goerli/usdc.e/relations'; import arbitrumGoerliNativeUsdcRelationConfigMap from './deployments/arbitrum-goerli/usdc/relations'; import arbitrumUsdtRelationConfigMap from './deployments/arbitrum/usdt/relations'; @@ -38,6 +39,7 @@ import baseGoerliRelationConfigMap from './deployments/base-goerli/usdc/relation import baseGoerliWethRelationConfigMap from './deployments/base-goerli/weth/relations'; import lineaGoerliRelationConfigMap from './deployments/linea-goerli/usdc/relations'; import optimismRelationConfigMap from './deployments/optimism/usdc/relations'; +import optimismUsdtRelationConfigMap from './deployments/optimism/usdt/relations'; import scrollGoerliRelationConfigMap from './deployments/scroll-goerli/usdc/relations'; import scrollRelationConfigMap from './deployments/scroll/usdc/relations'; @@ -355,7 +357,8 @@ const config: HardhatUserConfig = { arbitrum: { 'usdc.e': arbitrumBridgedUsdcRelationConfigMap, usdc: arbitrumNativeUsdcRelationConfigMap, - usdt: arbitrumUsdtRelationConfigMap + usdt: arbitrumUsdtRelationConfigMap, + weth: arbitrumWETHRelationConfigMap }, 'arbitrum-goerli': { 'usdc.e': arbitrumBridgedUsdcGoerliRelationConfigMap, @@ -374,7 +377,8 @@ const config: HardhatUserConfig = { usdc: lineaGoerliRelationConfigMap }, optimism: { - usdc: optimismRelationConfigMap + usdc: optimismRelationConfigMap, + usdt: optimismUsdtRelationConfigMap }, 'scroll-goerli': { usdc: scrollGoerliRelationConfigMap @@ -458,6 +462,12 @@ const config: HardhatUserConfig = { deployment: 'usdc', auxiliaryBase: 'mainnet' }, + { + name: 'arbitrum-weth', + network: 'arbitrum', + deployment: 'weth', + auxiliaryBase: 'mainnet' + }, { name: 'arbitrum-goerli-usdc.e', network: 'arbitrum-goerli', @@ -512,6 +522,12 @@ const config: HardhatUserConfig = { deployment: 'usdc', auxiliaryBase: 'mainnet' }, + { + name: 'optimism-usdt', + network: 'optimism', + deployment: 'usdt', + auxiliaryBase: 'mainnet', + }, { name: 'scroll-goerli', network: 'scroll-goerli', @@ -559,4 +575,4 @@ const config: HardhatUserConfig = { setupDefaultNetworkProviders(config); -export default config; +export default config; \ No newline at end of file diff --git a/scenario/BulkerScenario.ts b/scenario/BulkerScenario.ts index bfeba4c65..42597dda1 100644 --- a/scenario/BulkerScenario.ts +++ b/scenario/BulkerScenario.ts @@ -179,10 +179,11 @@ scenario( const baseAssetAddress = await comet.baseToken(); const baseAsset = context.getAssetByAddress(baseAssetAddress); const baseScale = (await comet.baseScale()).toBigInt(); + // if asset 0 is native token we took asset 1 const { asset: asset0, scale: scale0 } = await comet.getAssetInfo(0); const { asset: asset1, scale: scale1 } = await comet.getAssetInfo(1); - const { asset: collateralAssetAddress, scale: scaleBN } = asset0 === wrappedNativeToken ? { asset: asset1, scale: scale1 } : { asset: asset0, scale: scale0 }; const collateralAsset = context.getAssetByAddress(collateralAssetAddress); - console.log('collateralAsset', collateralAssetAddress); + const { asset: collateralAssetAddress, scale: scaleBN } = asset0 === wrappedNativeToken ? { asset: asset1, scale: scale1 } : { asset: asset0, scale: scale0 }; + const collateralAsset = context.getAssetByAddress(collateralAssetAddress); const collateralScale = scaleBN.toBigInt(); const [rewardTokenAddress] = await rewards.rewardConfig(comet.address); const toSupplyBase = 1_000_000n * baseScale; diff --git a/scenario/utils/relayArbitrumMessage.ts b/scenario/utils/relayArbitrumMessage.ts index 085b991c1..442d45a27 100644 --- a/scenario/utils/relayArbitrumMessage.ts +++ b/scenario/utils/relayArbitrumMessage.ts @@ -3,6 +3,7 @@ import { impersonateAddress } from '../../plugins/scenario/utils'; import { setNextBaseFeeToZero, setNextBlockTimestamp } from './hreUtils'; import { utils, BigNumber } from 'ethers'; import { Log } from '@ethersproject/abstract-provider'; +import { sourceTokens } from '../../plugins/scenario/utils/TokenSourcer'; export async function relayArbitrumMessage( governanceDeploymentManager: DeploymentManager, @@ -87,6 +88,25 @@ export async function relayArbitrumMessage( bridgeDeploymentManager, sender ); + // if method name == finalizeInboundTransfer(address,address,address,uint256,bytes) + if(data.slice(0, 10) == '0x2e567b36'){ + const _data = '0x' + data.slice(10, 266); + const [token,, to, amount] = utils.defaultAbiCoder.decode( + ['address', 'address', 'address', 'uint256'], + _data + ); + // if token is mainnet ETH -> than source arbitrum weth + if(token == '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'){ + await sourceTokens({ + dm: bridgeDeploymentManager, + amount: amount, + asset: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + address: to, + blacklist: [], + }); + continue; + } + } const transactionRequest = await arbitrumSigner.populateTransaction({ to: toAddress, from: sender, diff --git a/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts b/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts index 233825cba..74cb6e25f 100644 --- a/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts +++ b/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts @@ -56,7 +56,10 @@ const addresses = { WBTC: '0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f', USDC: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', USDC_E: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', - USDT: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' + USDT: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', + rETH: '0xEC70Dcb4A1EFa46b8F2D97C310C9c4790ba5ffA8', + wstETH: '0x5979D7b546E38E414F7E9822514be443A4800529', + weETH: '0x35751007a407ca6FEFfE80b3cB397736D2cf4dbe' } }; @@ -112,6 +115,10 @@ export const flashLoanPools = { usdt: { tokenAddress: addresses.arbitrum.USDT, poolFee: 100 + }, + weth: { + tokenAddress: addresses.arbitrum.USDC, + poolFee: 500 } } }; @@ -236,6 +243,28 @@ export function getPoolConfig(tokenAddress: string) { uniswapPoolFee: 500 } }, + [addresses.arbitrum.rETH.toLowerCase()]: { + ...defaultPoolConfig, + ...{ + exchange: Exchange.Balancer, + balancerPoolId: '0xd0ec47c54ca5e20aaae4616c25c825c7f48d40690000000000000000000004ef' + } + }, + [addresses.arbitrum.wstETH.toLowerCase()]: { + ...defaultPoolConfig, + ...{ + exchange: Exchange.Balancer, + balancerPoolId: '0x9791d590788598535278552eecd4b211bfc790cb000000000000000000000498' + } + }, + [addresses.arbitrum.weETH.toLowerCase()]: { + ...defaultPoolConfig, + ...{ + exchange: Exchange.Uniswap, + swapViaWeth: false, + uniswapPoolFee: 100 + } + }, }; const poolConfig = poolConfigs[tokenAddress.toLowerCase()]; @@ -263,7 +292,9 @@ function getMaxAmountToPurchase(tokenAddress: string): bigint { [addresses.arbitrum.ARB.toLowerCase()]: exp(500000, 18), [addresses.arbitrum.GMX.toLowerCase()]: exp(4000, 18), [addresses.arbitrum.WETH.toLowerCase()]: exp(2000, 18), - [addresses.arbitrum.WBTC.toLowerCase()]: exp(100, 8) + [addresses.arbitrum.WBTC.toLowerCase()]: exp(100, 8), + [addresses.arbitrum.rETH.toLowerCase()]: exp(2000, 18), + [addresses.arbitrum.wstETH.toLowerCase()]: exp(2000, 18) }; const max = maxAmountsToPurchase[tokenAddress.toLowerCase()]; diff --git a/src/deploy/index.ts b/src/deploy/index.ts index 209bfff19..f912c38bf 100644 --- a/src/deploy/index.ts +++ b/src/deploy/index.ts @@ -96,9 +96,21 @@ export const WHALES = { '0x167384319b41f7094e62f7506409eb38079abff8' // WMATIC whale ], arbitrum: [ + '0x8eb270e296023e9d92081fdf967ddd7878724424', // rETH whale + '0x78e88887d80451cb08fdc4b9046c9d01fb8d048d', // rETH whale + '0xc0cf4b266be5b3229c49590b59e67a09c15b22f4', // rETH whale + '0x84446698694b348eaece187b55df06ab4ce72b35', // rETH whale + '0x42c248d137512907048021b30d9da17f48b5b7b2', // wstETH whale + '0xc3e5607cd4ca0d5fe51e09b60ed97a0ae6f874dd', // WETH whale '0xf89d7b9c864f589bbf53a82105107622b35eaa40', // USDC whale '0x7b7b957c284c2c227c980d6e2f804311947b84d0', // WBTC whale - '0x88730d254A2f7e6AC8388c3198aFd694bA9f7fae', // COMP whale + '0x1c6b5795be43ddff8812b3e577ac752764635bc5', // COMP whale + '0xdead767ba9f8072c986a4619c27ae46bcc226c13', // COMP whale + '0xde5167c19a5286889752cb0f31a1c7f28a99fefb', // COMP whale + '0xdfa19e743421c394d904f5a113121c2227d2364b', // COMP whale + '0xee3273f6d29ddfff08ffd9d513cff314734f01a2', // COMP whale + '0x9e786a8fc88ee74b758b125071d45853356024c3', // COMP whale + '0xd93f76944e870900779c09ddf1c46275f9d8bf9b', // COMP whale '0xe68ee8a12c611fd043fb05d65e1548dc1383f2b9' // native USDC whale ], base: [