diff --git a/.github/workflows/enact-migration.yaml b/.github/workflows/enact-migration.yaml index 1b1558688..6842ba074 100644 --- a/.github/workflows/enact-migration.yaml +++ b/.github/workflows/enact-migration.yaml @@ -36,6 +36,10 @@ on: description: Run ID for Artifact eth_pk: description: Ignore if you plan to use WalletConnect, otherwise, you can paste in a Ethereum private key + impersonateAccount: + description: Impersonate Account + required: false + default: '' jobs: enact-migration: name: Enact Migration @@ -114,7 +118,18 @@ jobs: GOV_NETWORK_PROVIDER: ${{ fromJSON('["", "http://localhost:8685"]')[github.event.inputs.eth_pk == '' && env.GOV_NETWORK != ''] }} GOV_NETWORK: ${{ env.GOV_NETWORK }} REMOTE_ACCOUNTS: ${{ fromJSON('["", "true"]')[github.event.inputs.eth_pk == ''] }} - + if: github.event.inputs.impersonateAccount == '' + - name: Run Enact Migration (impersonate) + run: | + yarn hardhat migrate --network ${{ github.event.inputs.network }} --deployment ${{ github.event.inputs.deployment }} --enact --overwrite ${{ fromJSON('["", "--simulate"]')[github.event.inputs.simulate == 'true'] }} ${{ fromJSON('["", "--no-enacted"]')[github.event.inputs.no_enacted == 'true'] }} ${{ github.event.inputs.migration }} --impersonate ${{ github.event.inputs.impersonateAccount }} + env: + DEBUG: true + ETH_PK: "${{ inputs.eth_pk }}" + NETWORK_PROVIDER: ${{ fromJSON('["", "http://localhost:8585"]')[github.event.inputs.eth_pk == ''] }} + GOV_NETWORK_PROVIDER: ${{ fromJSON('["", "http://localhost:8685"]')[github.event.inputs.eth_pk == '' && env.GOV_NETWORK != ''] }} + GOV_NETWORK: ${{ env.GOV_NETWORK }} + REMOTE_ACCOUNTS: ${{ fromJSON('["", "true"]')[github.event.inputs.eth_pk == ''] }} + if: github.event.inputs.impersonateAccount != '' - name: Commit changes if: ${{ github.event.inputs.simulate == 'false' }} run: | diff --git a/.github/workflows/run-scenarios.yaml b/.github/workflows/run-scenarios.yaml index 7c551a883..604306d9b 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, 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, polygon-usdt, 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] name: Run scenarios env: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} diff --git a/deployments/polygon/usdt/configuration.json b/deployments/polygon/usdt/configuration.json new file mode 100644 index 000000000..e95b34b66 --- /dev/null +++ b/deployments/polygon/usdt/configuration.json @@ -0,0 +1,76 @@ +{ + "name": "Compound USDT", + "symbol": "cUSDTv3", + "baseToken": "USDT", + "baseTokenAddress": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F", + "baseTokenPriceFeed": "0x0A6513e40db6EB1b165753AD52E80663aeA50545", + "borrowMin": "1e6", + "governor": "0xCC3E7c85Bb0EE4f09380e041fee95a0caeDD4a02", + "pauseGuardian": "0x8Ab717CAC3CbC4934E63825B88442F5810aAF6e5", + "storeFrontPriceFactor": 0.6, + "targetReserves": "20000000e6", + "rates": { + "supplyKink": 0.9, + "supplySlopeLow": 0.075, + "supplySlopeHigh": 3.6, + "supplyBase": 0, + "borrowKink": 0.9, + "borrowSlopeLow": 0.0833, + "borrowSlopeHigh": 4.3, + "borrowBase": 0.015 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "0e0", + "baseBorrowSpeed": "0e0", + "baseMinForRewards": "1000e6" + }, + "rewardTokenAddress": "0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c", + "assets": { + "WMATIC": { + "address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "priceFeed": "0xAB594600376Ec9fD91F8e885dADF0CE036862dE0", + "decimals": "18", + "borrowCF": 0.65, + "liquidateCF": 0.80, + "liquidationFactor": 0.85, + "supplyCap": "0e18" + }, + "WETH": { + "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + "priceFeed": "0xF9680D99D6C9589e2a93a78A04A279e509205945", + "decimals": "18", + "borrowCF": 0.80, + "liquidateCF": 0.85, + "liquidationFactor": 0.95, + "supplyCap": "0e18" + }, + "MaticX": { + "address": "0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6", + "priceFeed": "0x5d37E4b374E6907de8Fc7fb33EE3b0af403C7403", + "decimals": "18", + "borrowCF": 0.60, + "liquidateCF": 0.70, + "liquidationFactor": 0.80, + "supplyCap": "0e18" + }, + "stMATIC": { + "address": "0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4", + "priceFeed": "0x97371dF4492605486e23Da797fA68e55Fc38a13f", + "decimals": "18", + "borrowCF": 0.60, + "liquidateCF": 0.70, + "liquidationFactor": 0.80, + "supplyCap": "0e18" + }, + "WBTC": { + "address": "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6", + "priceFeed": "0xDE31F8bFBD8c84b5360CFACCa3539B938dd78ae6", + "decimals": "8", + "borrowCF": 0.75, + "liquidateCF": 0.85, + "liquidationFactor": 0.90, + "supplyCap": "0e8" + } + } +} \ No newline at end of file diff --git a/deployments/polygon/usdt/deploy.ts b/deployments/polygon/usdt/deploy.ts new file mode 100644 index 000000000..cc7989ddc --- /dev/null +++ b/deployments/polygon/usdt/deploy.ts @@ -0,0 +1,39 @@ +import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet } from '../../../src/deploy'; + +export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise { + const trace = deploymentManager.tracer() + const ethers = deploymentManager.hre.ethers; + + // pull in existing assets + const USDT = await deploymentManager.existing('USDT', '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', 'polygon'); + const WBTC = await deploymentManager.existing('WBTC', '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6', 'polygon'); + const WETH = await deploymentManager.existing('WETH', '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', 'polygon'); + const WMATIC = await deploymentManager.existing('WMATIC', '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 'polygon'); + const MaticX = await deploymentManager.existing('MaticX', '0xfa68FB4628DFF1028CFEc22b4162FCcd0d45efb6', 'polygon'); + const stMATIC = await deploymentManager.existing('stMATIC', '0x3A58a54C066FdC0f2D55FC9C89F0415C92eBf3C4', 'polygon'); + const COMP = await deploymentManager.existing('COMP', '0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c', 'polygon'); + + const fxChild = await deploymentManager.existing('fxChild', '0x8397259c983751DAf40400790063935a11afa28a', 'polygon'); + + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'polygon', 'usdc'); + const cometFactory = await deploymentManager.fromDep('cometFactory', 'polygon', 'usdc'); + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'polygon', 'usdc'); + const configurator = await deploymentManager.fromDep('configurator', 'polygon', 'usdc'); + const rewards = await deploymentManager.fromDep('rewards', 'polygon', 'usdc'); + const bulker = await deploymentManager.fromDep('bulker', 'polygon', 'usdc'); + const localTimelock = await deploymentManager.fromDep('timelock', 'polygon', 'usdc'); + const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'polygon', 'usdc'); + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + + return { + ...deployed, + bridgeReceiver, + bulker, + fxChild, + rewards, + COMP + }; +} \ No newline at end of file diff --git a/deployments/polygon/usdt/migrations/1713283675_configurate_and_ens.ts b/deployments/polygon/usdt/migrations/1713283675_configurate_and_ens.ts new file mode 100644 index 000000000..738d67759 --- /dev/null +++ b/deployments/polygon/usdt/migrations/1713283675_configurate_and_ens.ts @@ -0,0 +1,319 @@ +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 { diffState, getCometConfig } from '../../../../plugins/deployment_manager/DiffState'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; + +const ERC20PredicateAddress = '0x40ec5B33f54e0E8A33A975908C5BA1c14e5BbbDf'; +const RootChainManagerAddress = '0xA0c68C638235ee32657e8f720a23ceC1bFc77C77'; + +const mainnetUSDTAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'; +const polygonCOMPAddress = '0x8505b9d2254A7Ae468c0E9dd10Ccea3A837aef5c'; +const cUSDTAddress = '0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9'; + +const USDTAmountToBridge = ethers.BigNumber.from(exp(10_000, 6)); + +export default migration('1713283675_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', 'polygon', 'usdc'); + const { + bridgeReceiver, + comet, + cometAdmin, + configurator, + rewards, + USDT, + WBTC, + WETH, + WMATIC + } = await deploymentManager.getContracts(); + + const { + fxRoot, + timelock, + governor + } = await govDeploymentManager.getContracts(); + + 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, polygonCOMPAddress] + ); + + 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: 'USDT', cometAddress: comet.address }; + const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey)); + + // add arbitrum-usdt comet (0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07) + // arbitrum chain id is 42161 + if (!(officialMarketsJSON[42161].find(market => market.baseSymbol === 'USDT'))) { + officialMarketsJSON[42161].push({ baseSymbol: 'USDT', cometAddress: '0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07' }); + } + + // add arbitrum-weth comet (0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486) + // arbitrum chain id is 42161 + if (!(officialMarketsJSON[42161].find(market => market.baseSymbol === 'WETH'))) { + officialMarketsJSON[42161].push({ baseSymbol: 'WETH', cometAddress: '0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486' }); + } + + // add optimism-usdt comet (0x995E394b8B2437aC8Ce61Ee0bC610D617962B214) + // optimism chain id is 10 + if (!(officialMarketsJSON[10].find(market => market.baseSymbol === 'USDT'))) { + officialMarketsJSON[10].push({ baseSymbol: 'USDT', cometAddress: '0x995E394b8B2437aC8Ce61Ee0bC610D617962B214' }); + } + + if (officialMarketsJSON[polygonChainId]) { + officialMarketsJSON[polygonChainId].push(newMarketObject); + } else { + officialMarketsJSON[polygonChainId] = [newMarketObject]; + } + + const RootChainManager = await deploymentManager.existing( + 'RootChainManager', + RootChainManagerAddress + ); + const USDTMainnet = new Contract( + mainnetUSDTAddress, + [ + 'function balanceOf(address account) external view returns (uint256)', + 'function approve(address,uint256) external' + ], + govDeploymentManager.hre.ethers.provider + ); + + const depositUSDTData = utils.defaultAbiCoder.encode(['uint256'], [USDTAmountToBridge]); + const depositForUSDTCalldata = utils.defaultAbiCoder.encode( + ['address', 'address', 'bytes'], + [comet.address, USDTMainnet.address, depositUSDTData] + ); + 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] + ); + + const mainnetActions = [ + // 1. Set Comet configuration and deployAndUpgradeTo new Comet on Polygon. + { + contract: fxRoot, + signature: 'sendMessageToChild(address,bytes)', + args: [bridgeReceiver.address, l2ProposalData] + }, + // 2. Get USDT reserves from cUSDT contract + { + target: cUSDTAddress, + signature: '_reduceReserves(uint256)', + calldata: _reduceReservesCalldata + }, + // 3. Approve Polygon's ERC20Predicate to take Timelock's USDT (for bridging) + { + contract: USDTMainnet, + signature: 'approve(address,uint256)', + args: [ERC20PredicateAddress, USDTAmountToBridge] + }, + // 4. Bridge USDT from mainnet to Polygon Comet using RootChainManager + { + target: RootChainManager.address, + signature: 'depositFor(address,address,bytes)', + calldata: depositForUSDTCalldata + }, + // 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 cUSDTv3 on Polygon\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes the deployment of Compound III to the Polygon network. This proposal takes the governance steps recommended and necessary to initialize a Compound III USDT market on Polygon; 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/add-market-usdt-on-polygon/5190/3).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/858), [market deployment action](https://github.com/woof-software/comet/actions/runs/9365232640) and [forum discussion](https://www.comp.xyz/t/add-market-usdt-on-polygon/5190).\n\n\n## Proposal Actions\n\nThe first proposal action sets the Comet configuration and deploys a new Comet implementation on Polygon. This sends the encoded `setFactory`, `setConfiguration` and `deployAndUpgradeTo` calls across the bridge to the governance receiver on Polygon. It also calls `setRewardConfig` on the Polygon rewards contract, to establish Polygon’s bridged version of COMP as the reward token for the deployment and set the initial supply speed to be 8 COMP/day and borrow speed to be 4 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 Polygon RootChainManager.\n\nThe third action approves Polygon’s [RootChainManager](https://etherscan.io/address/0xA0c68C638235ee32657e8f720a23ceC1bFc77C77) 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 Polygon RootChainManager 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 Polygon cUSDTv3 market"; + 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 false; + }, + + async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) { + const ethers = deploymentManager.hre.ethers; + + const { + comet, + rewards, + WMATIC, + WETH, + MaticX, + stMATIC, + WBTC, + COMP + } = await deploymentManager.getContracts(); + + const { + timelock + } = await govDeploymentManager.getContracts(); + + const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); + // expect(stateChanges).to.deep.equal({ + // WMATIC: { + // supplyCap: exp(5_000_000, 18) + // }, + // WETH: { + // supplyCap: exp(2_000, 18) + // }, + // MaticX: { + // supplyCap: exp(2_600_000, 18), + // }, + // stMATIC: { + // supplyCap: exp(1_500_000, 18) + // }, + // WBTC: { + // supplyCap: exp(90, 8) + // }, + // baseTrackingSupplySpeed: exp(8 / 86400, 15, 18), + // baseTrackingBorrowSpeed: exp(4 / 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', + }, + { + baseSymbol: 'USDT', + cometAddress: comet.address, + } + ], + 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: '0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486', + }, + { + baseSymbol: 'USDT', + cometAddress: '0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07', + }, + ], + 534352: [ + { + baseSymbol: 'USDC', + cometAddress: '0xB2f97c1Bd3bf02f5e74d13f02E3e26F93D77CE44', + }, + ], + 10: [ + { + baseSymbol: 'USDC', + cometAddress: '0x2e44e174f7D53F0212823acC11C01A11d58c5bCB', + }, + { + baseSymbol: 'USDT', + cometAddress: '0x995E394b8B2437aC8Ce61Ee0bC610D617962B214', + }, + ], + }); + } +}); diff --git a/deployments/polygon/usdt/relations.ts b/deployments/polygon/usdt/relations.ts new file mode 100644 index 000000000..d5ff91f97 --- /dev/null +++ b/deployments/polygon/usdt/relations.ts @@ -0,0 +1,25 @@ +import { RelationConfigMap } from '../../../plugins/deployment_manager/RelationConfig'; +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + 'governor': { + artifact: 'contracts/bridges/polygon/PolygonBridgeReceiver.sol:PolygonBridgeReceiver', + }, + InitializableImmutableAdminUpgradeabilityProxy: { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + UChildERC20Proxy: { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0xbaab7dbf64751104133af04abc7d9979f0fda3b059a322a8333f533d3f32bf7f', + } + }, + } +}; \ No newline at end of file diff --git a/deployments/polygon/usdt/roots.json b/deployments/polygon/usdt/roots.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/deployments/polygon/usdt/roots.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 284c8cb5c..1c4280a46 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -26,6 +26,7 @@ import mumbaiRelationConfigMap from './deployments/mumbai/usdc/relations'; import mainnetRelationConfigMap from './deployments/mainnet/usdc/relations'; import mainnetWethRelationConfigMap from './deployments/mainnet/weth/relations'; import polygonRelationConfigMap from './deployments/polygon/usdc/relations'; +import polygonUsdtRelationConfigMap from './deployments/polygon/usdt/relations'; import arbitrumBridgedUsdcRelationConfigMap from './deployments/arbitrum/usdc.e/relations'; import arbitrumNativeUsdcRelationConfigMap from './deployments/arbitrum/usdc/relations'; import arbitrumBridgedUsdcGoerliRelationConfigMap from './deployments/arbitrum-goerli/usdc.e/relations'; @@ -349,7 +350,8 @@ const config: HardhatUserConfig = { weth: mainnetWethRelationConfigMap }, polygon: { - usdc: polygonRelationConfigMap + usdc: polygonRelationConfigMap, + usdt: polygonUsdtRelationConfigMap }, arbitrum: { 'usdc.e': arbitrumBridgedUsdcRelationConfigMap, @@ -438,6 +440,12 @@ const config: HardhatUserConfig = { deployment: 'usdc', auxiliaryBase: 'mainnet' }, + { + name: 'polygon-usdt', + network: 'polygon', + deployment: 'usdt', + auxiliaryBase: 'mainnet' + }, { name: 'arbitrum-usdc.e', network: 'arbitrum', diff --git a/scenario/AddMaticxCollateralScenario.ts b/scenario/AddMaticxCollateralScenario.ts index b28f1aabd..783e58d9b 100644 --- a/scenario/AddMaticxCollateralScenario.ts +++ b/scenario/AddMaticxCollateralScenario.ts @@ -14,11 +14,14 @@ const MATICX_WHALES = { // TODO: add ability to run ad hoc scenarios against a single migration, to avoid needing the scenario to do all this setup of // listing an asset + +// This scenario should only run for polygon usdc, cause it simulates adding of the new asset +// It could be removed at all, because all scenarios will run for new collateral. For that should be crated migration script with enacted: false +// While running the scenario, it checks all not enacted migrations, creates proposal, executes it and only after it starts simulations scenario( 'add new asset maticx', { - filter: async (ctx) => - matchesDeployment(ctx, [{ network: 'polygon' }]), + filter: async (ctx) => matchesDeployment(ctx, [{ network: 'polygon', deployment: 'usdc' }]), tokenBalances: { $comet: { $base: '>= 1' }, }, diff --git a/scenario/BulkerScenario.ts b/scenario/BulkerScenario.ts index 91e2608f8..7c9f39908 100644 --- a/scenario/BulkerScenario.ts +++ b/scenario/BulkerScenario.ts @@ -14,7 +14,7 @@ scenario( $asset1: 3000, }, tokenBalances: { - albert: { $base: '== 0', $asset0: 3000 }, + albert: { $base: '== 0', $asset0: 3000, $asset1: 3000 }, $comet: { $base: 5000 }, }, }, @@ -24,7 +24,10 @@ scenario( const baseAssetAddress = await comet.baseToken(); const baseAsset = context.getAssetByAddress(baseAssetAddress); const baseScale = (await comet.baseScale()).toBigInt(); - const { asset: collateralAssetAddress, scale: scaleBN } = await comet.getAssetInfo(0); + // 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); const collateralScale = scaleBN.toBigInt(); const toSupplyCollateral = 3000n * collateralScale; @@ -165,9 +168,10 @@ scenario( filter: async (ctx) => await isBulkerSupported(ctx) && await isRewardSupported(ctx) && !matchesDeployment(ctx, [{deployment: 'weth'}, { network: 'linea-goerli' }]), supplyCaps: { $asset0: 3000, + $asset1: 3000, }, tokenBalances: { - albert: { $base: '== 1000000', $asset0: 3000 }, + albert: { $base: '== 1000000', $asset0: 3000, $asset1: 3000}, $comet: { $base: 5000 }, } }, @@ -177,7 +181,10 @@ scenario( const baseAssetAddress = await comet.baseToken(); const baseAsset = context.getAssetByAddress(baseAssetAddress); const baseScale = (await comet.baseScale()).toBigInt(); - const { asset: collateralAssetAddress, scale: scaleBN } = await comet.getAssetInfo(0); + // 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); const collateralScale = scaleBN.toBigInt(); const [rewardTokenAddress] = await rewards.rewardConfig(comet.address); diff --git a/scenario/LiquidationBotScenario.ts b/scenario/LiquidationBotScenario.ts index 763468c9e..0a0e51234 100644 --- a/scenario/LiquidationBotScenario.ts +++ b/scenario/LiquidationBotScenario.ts @@ -74,8 +74,9 @@ async function canBeLiquidatedByBot(ctx: CometContext, assetNum: number): Promis // Reason: Most liquidity lives in MATICX / MATIC pools, which the liquidation bot cannot use if the base asset is not MATIC MaticX: { network: 'polygon', - deployments: ['usdc'] - } + deployments: ['usdc', 'usdt'] + }, + }; const comet = await ctx.getComet(); const assetInfo = await comet.getAssetInfo(assetNum); diff --git a/scenario/SupplyScenario.ts b/scenario/SupplyScenario.ts index a3ec668af..4121a1865 100644 --- a/scenario/SupplyScenario.ts +++ b/scenario/SupplyScenario.ts @@ -309,7 +309,8 @@ scenario( /ERC20: insufficient allowance/, /transfer amount exceeds spender allowance/, /Dai\/insufficient-allowance/, - symbol === 'WETH' ? /Transaction reverted without a reason string/ : /.^/ + symbol === 'WETH' ? /Transaction reverted without a reason string/ : /.^/, + symbol === 'WMATIC' ? /Transaction reverted without a reason string/ : /.^/ ] ); } @@ -393,7 +394,8 @@ scenario( [ /transfer amount exceeds balance/, /Dai\/insufficient-balance/, - symbol === 'WETH' ? /Transaction reverted without a reason string/ : /.^/ + symbol === 'WETH' ? /Transaction reverted without a reason string/ : /.^/, + symbol === 'WMATIC' ? /Transaction reverted without a reason string/ : /.^/ ] ); } diff --git a/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts b/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts index d073fb587..a0746a263 100644 --- a/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts +++ b/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts @@ -99,6 +99,10 @@ export const flashLoanPools = { usdc: { tokenAddress: addresses.polygon.BOB, poolFee: 100 + }, + usdt: { + tokenAddress: addresses.polygon.WBTC, + poolFee: 500 } }, arbitrum: {