From 9a962f0389a3f7d21d708dce0d4b735032b60466 Mon Sep 17 00:00:00 2001 From: Hans Wang <2709448+cwang25@users.noreply.github.com> Date: Fri, 21 Jul 2023 12:24:34 -0700 Subject: [PATCH] Deploy and proposal for native USDC market on Arbitrum Goerli (#793) * Deploy Arbitrum goerli for usdc native (#778) * initial gen * remove files * rename directory * add scenario * upadte deploy script * renamde to usdc.n * added relations for scenario test * add scenario test to CI * add migration scrtips, but just changed the USDC reference address * update migration script * since it's native, we shouldn't need usdc bridge things in migration * try to fix the liquidator scenario errors * intent * change those to be 0 initially * FINALLY scneario passed :D * don't re-deploy bulker * set a number in configuration to make bulker tests pass * set configuration to 0 supply cap for initialization, although it will fail the bulker test * update liquidity pool to use the one with better liquidity * after new pool use better liquidity, now able to bump up the volume * remove v2 rewards config step, crank down borrowMin * update migration description * remove trailing newline * ok need to referencing existing rewards in deploy script to share the rewards' * naming changes * added cctp contracts address to roots and update relayArbitrummessage * add l2 cctp * depositForBurn() to use CCTP to send native USDC to arbitrum comet * finally relayArbitrum works with CCTP * format * update to turn off bridge market's reward speed * fix scenarios and slightly reduced volume in liquidator scenarios (somehow during testing the pool the bot uses to flashloan have been withdrew and don't have full liquidity to cover tests * now it has just enough liquidity to pass the test, reverted the liquidation scenario back to usual amount * address comments: remove network prefixes, move contract import from deploy to migration scripts, move relay specific contracts to relay script * remove network prefixes, and rename bridgeComet to usdceComet * rename/refactor usdc -> usdc.e, and usdc.n -> usdc. Also rename deplyoment name for to and to * luckily by the time of testing now Jul 6th 1:40PM PST, we have enough liquidity in the uniswap USDC.e:USDC pool to cover the unchanged volume liquidation scenarios test * rename usdc.n to usdc * make yarn lint happy * rename bridged version to .e to match with mainnet * ported changes to testnet * update on workflow * hmm the error is gone * change default supply reward speed on new comet to be 0 * move migration steps out of this branch * add readme.md to corner cases when deploy new market on the chain with an existing market already * add line * removed USDC in relations * update version to unblock deploy workerflow for now * use dummy requested_network id * try arbitrum goerli network id right away * change a bit on how it locates the hardhat config base * remove hardcoded network chain-id * no more manual wallet project id input * use env.GOV_NETWORK * GOV_NETWORK got some errors :[ * try this seacrest with fixes * try new seacrest :D * Modified deployment roots from GitHub Actions * Migrate Arbitrum Goerli Native usdc (#779) * migration scripts commits * set supply and borrow speeds to be non zero * add diff check * add diff checker * added comments on checks * now scenario run is passing, but with the cost of removing linea's contract addresses from goerli/roots.json * add linea back, but scenario run will fail. But it's not related to arbitrum-goerli * Upgrade git action to use new seacrest (#782) * upgrade git action to use new seacrest * Fix unverified Linea Goerli contract issue (#787) * addressed comments * first initial working version of CCTP attestation script to help check with circle attestation status to mint native USDC to destination * attestation script with sending txn on user behalf * fix lint * fix lint * Modified migration from GitHub Actions --------- Co-authored-by: Kevin Cheng Co-authored-by: GitHub Actions Bot <> * removed asdf config file that should've been ignored * add native usdc whale for arbitrum goerli --------- Co-authored-by: GitHub Actions Bot <> Co-authored-by: Kevin Cheng * revert some mixed up changes when extracted from #769 * fix name * update reference path * fix lint * Update hardhat.config.ts Co-authored-by: Kevin Cheng * Update hardhat.config.ts Co-authored-by: Kevin Cheng * fix error --------- Co-authored-by: Kevin Cheng --- .github/workflows/run-scenarios.yaml | 2 +- README.md | 15 +- .../arbitrum-goerli/usdc.e/configuration.json | 56 ++++ deployments/arbitrum-goerli/usdc.e/deploy.ts | 70 +++++ .../1679518383_configurate_and_ens.ts | 0 .../arbitrum-goerli/usdc.e/relations.ts | 29 ++ deployments/arbitrum-goerli/usdc.e/roots.json | 7 + .../arbitrum-goerli/usdc/configuration.json | 8 +- deployments/arbitrum-goerli/usdc/deploy.ts | 57 +--- .../1689112067_configurate_and_ens.ts | 283 ++++++++++++++++++ deployments/arbitrum-goerli/usdc/relations.ts | 9 + deployments/arbitrum-goerli/usdc/roots.json | 5 +- deployments/goerli/usdc/roots.json | 4 +- hardhat.config.ts | 14 +- scripts/CCTP-attestation.ts | 65 ++++ src/deploy/index.ts | 3 +- tasks/deployment_manager/task.ts | 8 +- 17 files changed, 569 insertions(+), 66 deletions(-) create mode 100644 deployments/arbitrum-goerli/usdc.e/configuration.json create mode 100644 deployments/arbitrum-goerli/usdc.e/deploy.ts rename deployments/arbitrum-goerli/{usdc => usdc.e}/migrations/1679518383_configurate_and_ens.ts (100%) create mode 100644 deployments/arbitrum-goerli/usdc.e/relations.ts create mode 100644 deployments/arbitrum-goerli/usdc.e/roots.json create mode 100644 deployments/arbitrum-goerli/usdc/migrations/1689112067_configurate_and_ens.ts create mode 100644 scripts/CCTP-attestation.ts diff --git a/.github/workflows/run-scenarios.yaml b/.github/workflows/run-scenarios.yaml index 4e954f580..9f154834d 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, fuji, mumbai, polygon, arbitrum, arbitrum-goerli, base-goerli, base-goerli-weth, linea-goerli ] + bases: [ development, mainnet, mainnet-weth, goerli, goerli-weth, fuji, mumbai, polygon, arbitrum, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-goerli, base-goerli-weth, linea-goerli] name: Run scenarios env: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} diff --git a/README.md b/README.md index c345d47c1..fd284b2ed 100644 --- a/README.md +++ b/README.md @@ -232,11 +232,16 @@ These simulations are extremely useful for testing deployments before actually c #### Deploy Workflow -0. Create the deployment script and configuration file, and test locally -1. Open a PR containing the new deployment directory files -2. Trigger the `deploy-market` workflow action through the GitHub UI -3. Inspect the new `roots.json` which the workflow automatically commited to your PR -4. Start using the new protocol deployment and/or create further migrations to modify it +1. Create the deployment script and configuration file, and test locally +2. Open a PR containing the new deployment directory files +3. Trigger the `deploy-market` workflow action through the GitHub UI +4. Inspect the new `roots.json` which the workflow automatically commited to your PR +5. Start using the new protocol deployment and/or create further migrations to modify it + +##### Deploy Script Gotchas and Tips + +- If the deploy script is for a new market on a chain with an existing market, make sure to call 'setFactory(address,address)' in the initialization migration script. +(TODO: Scenarios will fail prior to running the migration script because the factory will not be set during deployment, will need to figure out a better way) ##### Verifying Deployments diff --git a/deployments/arbitrum-goerli/usdc.e/configuration.json b/deployments/arbitrum-goerli/usdc.e/configuration.json new file mode 100644 index 000000000..1d759f556 --- /dev/null +++ b/deployments/arbitrum-goerli/usdc.e/configuration.json @@ -0,0 +1,56 @@ +{ + "name": "Compound USDC", + "symbol": "cUSDCv3", + "baseToken": "USDC.e", + "baseTokenAddress": "0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892", + "baseTokenPriceFeed": "0x1692Bdd32F31b831caAc1b0c9fAF68613682813b", + "borrowMin": "100e6", + "pauseGuardian": "0x6C2fD6738e43ce0cc4A6f23a37e3a733516794A0", + "storeFrontPriceFactor": 0.5, + "targetReserves": "5000000e6", + "rates": { + "supplyKink": 0.8, + "supplySlopeLow": 0.0325, + "supplySlopeHigh": 0.4, + "supplyBase": 0, + "borrowKink": 0.8, + "borrowSlopeLow": 0.035, + "borrowSlopeHigh": 0.25, + "borrowBase": 0.015 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "0e15", + "baseBorrowSpeed": "0e15", + "baseMinForRewards": "10000e6" + }, + "assets": { + "LINK": { + "address": "0xbb7303602be1b9149b097aafb094ffce1860e532", + "priceFeed": "0xd28Ba6CA3bB72bF371b80a2a0a33cBcf9073C954", + "decimals": "18", + "borrowCF": 0.775, + "liquidateCF": 0.825, + "liquidationFactor": 0.95, + "supplyCap": "5000000e18" + }, + "WETH": { + "address": "0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3", + "priceFeed": "0x62CAe0FA2da220f43a51F86Db2EDb36DcA9A5A08", + "decimals": "18", + "borrowCF": 0.775, + "liquidateCF": 0.825, + "liquidationFactor": 0.95, + "supplyCap": "5000e18" + }, + "WBTC": { + "address": "0x22d5e2dE578677791f6c90e0110Ec629be9d5Fb5", + "priceFeed": "0x6550bc2301936011c1334555e62A87705A81C12C", + "decimals": "8", + "borrowCF": 0.7, + "liquidateCF": 0.75, + "liquidationFactor": 0.93, + "supplyCap": "300e8" + } + } +} diff --git a/deployments/arbitrum-goerli/usdc.e/deploy.ts b/deployments/arbitrum-goerli/usdc.e/deploy.ts new file mode 100644 index 000000000..4d6e0cf67 --- /dev/null +++ b/deployments/arbitrum-goerli/usdc.e/deploy.ts @@ -0,0 +1,70 @@ +import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet } from '../../../src/deploy'; + +const SECONDS_PER_DAY = 24 * 60 * 60; + +const GOERLI_TIMELOCK = '0x8Fa336EB4bF58Cfc508dEA1B0aeC7336f55B1399'; + +export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise { + const trace = deploymentManager.tracer() + const ethers = deploymentManager.hre.ethers; + + // pull in existing assets + const USDC = await deploymentManager.existing('USDC', '0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892', 'arbitrum-goerli'); + const LINK = await deploymentManager.existing('LINK', '0xbb7303602be1b9149b097aafb094ffce1860e532', 'arbitrum-goerli'); + const WETH = await deploymentManager.existing('WETH', '0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3', 'arbitrum-goerli'); + const WBTC = await deploymentManager.existing('WBTC', '0x22d5e2dE578677791f6c90e0110Ec629be9d5Fb5', 'arbitrum-goerli'); + + // Deploy ArbitrumBridgeReceiver + const bridgeReceiver = await deploymentManager.deploy( + 'bridgeReceiver', + 'bridges/arbitrum/ArbitrumBridgeReceiver.sol', + [] + ); + + // Deploy Local Timelock + const localTimelock = await deploymentManager.deploy( + 'timelock', + 'vendor/Timelock.sol', + [ + bridgeReceiver.address, // admin + 10 * 60, // delay + 14 * SECONDS_PER_DAY, // grace period + 10 * 60, // minimum delay + 30 * SECONDS_PER_DAY // maximum delay + ] + ); + + // Initialize ArbitrumBridgeReceiver + await deploymentManager.idempotent( + async () => !(await bridgeReceiver.initialized()), + async () => { + trace(`Initializing BridgeReceiver`); + await bridgeReceiver.initialize( + GOERLI_TIMELOCK, // govTimelock + localTimelock.address // localTimelock + ); + trace(`BridgeReceiver initialized`); + } + ); + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + const { comet } = deployed; + + // Deploy Bulker + const bulker = await deploymentManager.deploy( + 'bulker', + 'bulkers/BaseBulker.sol', + [ + await comet.governor(), // admin + WETH.address // weth + ] + ); + + return { + ...deployed, + bridgeReceiver, + bulker + }; +} \ No newline at end of file diff --git a/deployments/arbitrum-goerli/usdc/migrations/1679518383_configurate_and_ens.ts b/deployments/arbitrum-goerli/usdc.e/migrations/1679518383_configurate_and_ens.ts similarity index 100% rename from deployments/arbitrum-goerli/usdc/migrations/1679518383_configurate_and_ens.ts rename to deployments/arbitrum-goerli/usdc.e/migrations/1679518383_configurate_and_ens.ts diff --git a/deployments/arbitrum-goerli/usdc.e/relations.ts b/deployments/arbitrum-goerli/usdc.e/relations.ts new file mode 100644 index 000000000..f266af2a2 --- /dev/null +++ b/deployments/arbitrum-goerli/usdc.e/relations.ts @@ -0,0 +1,29 @@ +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + governor: { + artifact: 'contracts/bridges/arbitrum/ArbitrumBridgeReceiver.sol:ArbitrumBridgeReceiver' + }, + ClonableBeaconProxy: { + artifact: 'contracts/ERC20.sol:ERC20' + }, + // USDC + '0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + // WETH + '0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + } +}; diff --git a/deployments/arbitrum-goerli/usdc.e/roots.json b/deployments/arbitrum-goerli/usdc.e/roots.json new file mode 100644 index 000000000..1ef25d7d2 --- /dev/null +++ b/deployments/arbitrum-goerli/usdc.e/roots.json @@ -0,0 +1,7 @@ +{ + "comet": "0x1d573274E19174260c5aCE3f2251598959d24456", + "configurator": "0x1Ead344570F0f0a0cD86d95d8adDC7855C8723Fb", + "rewards": "0x8DA65F8E3Aa22A498211fc4204C498ae9050DAE4", + "bridgeReceiver": "0xAC9fC1a9532BC92a9f33eD4c6Ce4A7a54930F376", + "bulker": "0x987350Af5a17b6DdafeB95E6e329c178f44841d7" +} \ No newline at end of file diff --git a/deployments/arbitrum-goerli/usdc/configuration.json b/deployments/arbitrum-goerli/usdc/configuration.json index 9281b2445..fda04e2ac 100644 --- a/deployments/arbitrum-goerli/usdc/configuration.json +++ b/deployments/arbitrum-goerli/usdc/configuration.json @@ -2,9 +2,9 @@ "name": "Compound USDC", "symbol": "cUSDCv3", "baseToken": "USDC", - "baseTokenAddress": "0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892", + "baseTokenAddress": "0xfd064A18f3BF249cf1f87FC203E90D8f650f2d63", "baseTokenPriceFeed": "0x1692Bdd32F31b831caAc1b0c9fAF68613682813b", - "borrowMin": "100e6", + "borrowMin": "1e6", "pauseGuardian": "0x6C2fD6738e43ce0cc4A6f23a37e3a733516794A0", "storeFrontPriceFactor": 0.5, "targetReserves": "5000000e6", @@ -20,8 +20,8 @@ }, "tracking": { "indexScale": "1e15", - "baseSupplySpeed": "0e15", - "baseBorrowSpeed": "0e15", + "baseSupplySpeed": "0.000402083333333e15", + "baseBorrowSpeed": "0.000402083333333e15", "baseMinForRewards": "10000e6" }, "assets": { diff --git a/deployments/arbitrum-goerli/usdc/deploy.ts b/deployments/arbitrum-goerli/usdc/deploy.ts index 4d6e0cf67..ce94cab24 100644 --- a/deployments/arbitrum-goerli/usdc/deploy.ts +++ b/deployments/arbitrum-goerli/usdc/deploy.ts @@ -10,61 +10,28 @@ export default async function deploy(deploymentManager: DeploymentManager, deplo const ethers = deploymentManager.hre.ethers; // pull in existing assets - const USDC = await deploymentManager.existing('USDC', '0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892', 'arbitrum-goerli'); + const USDC = await deploymentManager.existing('USDC', '0xfd064A18f3BF249cf1f87FC203E90D8f650f2d63', 'arbitrum-goerli'); const LINK = await deploymentManager.existing('LINK', '0xbb7303602be1b9149b097aafb094ffce1860e532', 'arbitrum-goerli'); const WETH = await deploymentManager.existing('WETH', '0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3', 'arbitrum-goerli'); const WBTC = await deploymentManager.existing('WBTC', '0x22d5e2dE578677791f6c90e0110Ec629be9d5Fb5', 'arbitrum-goerli'); - // Deploy ArbitrumBridgeReceiver - const bridgeReceiver = await deploymentManager.deploy( - 'bridgeReceiver', - 'bridges/arbitrum/ArbitrumBridgeReceiver.sol', - [] - ); - - // Deploy Local Timelock - const localTimelock = await deploymentManager.deploy( - 'timelock', - 'vendor/Timelock.sol', - [ - bridgeReceiver.address, // admin - 10 * 60, // delay - 14 * SECONDS_PER_DAY, // grace period - 10 * 60, // minimum delay - 30 * SECONDS_PER_DAY // maximum delay - ] - ); - - // Initialize ArbitrumBridgeReceiver - await deploymentManager.idempotent( - async () => !(await bridgeReceiver.initialized()), - async () => { - trace(`Initializing BridgeReceiver`); - await bridgeReceiver.initialize( - GOERLI_TIMELOCK, // govTimelock - localTimelock.address // localTimelock - ); - trace(`BridgeReceiver initialized`); - } - ); + // Import shared contracts from cUSDCv3 + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'arbitrum-goerli', 'usdc.e'); + const cometFactory = await deploymentManager.fromDep('cometFactory', 'arbitrum-goerli', 'usdc.e'); + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'arbitrum-goerli', 'usdc.e'); + const configurator = await deploymentManager.fromDep('configurator', 'arbitrum-goerli', 'usdc.e'); + const rewards = await deploymentManager.fromDep('rewards', 'arbitrum-goerli', 'usdc.e'); + const bulker = await deploymentManager.fromDep('bulker', 'arbitrum-goerli', 'usdc.e'); + const localTimelock = await deploymentManager.fromDep('timelock', 'arbitrum-goerli', 'usdc.e'); + const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'arbitrum-goerli', 'usdc.e'); // Deploy Comet const deployed = await deployComet(deploymentManager, deploySpec); - const { comet } = deployed; - - // Deploy Bulker - const bulker = await deploymentManager.deploy( - 'bulker', - 'bulkers/BaseBulker.sol', - [ - await comet.governor(), // admin - WETH.address // weth - ] - ); return { ...deployed, bridgeReceiver, - bulker + bulker, + rewards, }; } \ No newline at end of file diff --git a/deployments/arbitrum-goerli/usdc/migrations/1689112067_configurate_and_ens.ts b/deployments/arbitrum-goerli/usdc/migrations/1689112067_configurate_and_ens.ts new file mode 100644 index 000000000..4ba4b9935 --- /dev/null +++ b/deployments/arbitrum-goerli/usdc/migrations/1689112067_configurate_and_ens.ts @@ -0,0 +1,283 @@ +import { Contract } from 'ethers'; +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { diffState, getCometConfig } from '../../../../plugins/deployment_manager/DiffState'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { calldata, exp, getConfigurationStruct, proposal } from '../../../../src/deploy'; +import { applyL1ToL2Alias, estimateL2Transaction, estimateTokenBridge } from '../../../../scenario/utils/arbitrumUtils'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x19c2d5D0f035563344dBB7bE5fD09c8dad62b001'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; + +const arbitrumCOMPAddress = '0xf03370d2aCf26Dde26389B66498B7c293038F5aF'; + +export default migration('1689112067_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-goerli', 'usdc.e'); + const usdceComet = await deploymentManager.fromDep('usdceComet', 'arbitrum-goerli', 'usdc.e', 'comet'); + const { + bridgeReceiver, + timelock: l2Timelock, + comet, + cometAdmin, + configurator, + rewards, + } = await deploymentManager.getContracts(); + + const { + arbitrumInbox, + arbitrumL1GatewayRouter, + timelock, + governor, + USDC, + COMP, + CCTPTokenMessenger, + } = await govDeploymentManager.getContracts(); + + // CCTP destination domain for Arbitrum + const ArbitrumDestinationDomain = 3; + const USDCAmountToBridge = exp(10, 6); + const refundAddress = l2Timelock.address; + 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 turnOffUSDCeCometSupplySpeedCalldata = utils.defaultAbiCoder.encode( + ['address', 'uint64'], + [usdceComet.address, 0] + ); + + const turnOffUSDCeCometBorrowSpeedCalldata = utils.defaultAbiCoder.encode( + ['address', 'uint64'], + [usdceComet.address, 0] + ); + + const deployAndUpgradeToUSDCeCometCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, usdceComet.address] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [configurator.address, configurator.address, cometAdmin.address, rewards.address, configurator.address, configurator.address, cometAdmin.address], + [0, 0, 0, 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)', + 'setBaseTrackingSupplySpeed(address,uint64)', + 'setBaseTrackingBorrowSpeed(address,uint64)', + 'deployAndUpgradeTo(address,address)', + ], + [setFactoryCalldata, setConfigurationCalldata, deployAndUpgradeToCalldata, setRewardConfigCalldata, turnOffUSDCeCometSupplySpeedCalldata, turnOffUSDCeCometBorrowSpeedCalldata, deployAndUpgradeToUSDCeCometCalldata] + ] + ); + + const createRetryableTicketGasParams = await estimateL2Transaction( + { + from: applyL1ToL2Alias(timelock.address), + to: bridgeReceiver.address, + data: l2ProposalData + }, + deploymentManager + ); + + const ENSResolver = await govDeploymentManager.existing('ENSResolver', ENSResolverAddress, 'goerli'); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const arbitrumChainId = (await deploymentManager.hre.ethers.provider.getNetwork()).chainId.toString(); + const newMarketObject = { baseSymbol: 'USDC', cometAddress: comet.address }; + const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey)); + + // Rename old USDC market into USDC.e + officialMarketsJSON[arbitrumChainId][0].baseSymbol = 'USDC.e'; + + if (officialMarketsJSON[arbitrumChainId]) { + officialMarketsJSON[arbitrumChainId].push(newMarketObject); + } else { + officialMarketsJSON[arbitrumChainId] = [newMarketObject]; + } + + 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. Approve USDC to CCTP + { + contract: USDC, + signature: 'approve(address,uint256)', + args: [CCTPTokenMessenger.address, USDCAmountToBridge] + }, + // 3. Burn USDC to Arbitrum via CCTP + { + contract: CCTPTokenMessenger, + signature: 'depositForBurn(uint256,uint32,bytes32,address)', + args: [USDCAmountToBridge, ArbitrumDestinationDomain, utils.hexZeroPad(comet.address, 32), USDC.address], + }, + // 4. 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)] + ) + } + ]; + + // TODO: Will update this description to be more accurate once the contract is deployed + const description = "# Configurate Arbitrum cUSDCv3 market for Native USDC native, and set ENS record for official markets"; + 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, preMigrationBlockNumber: number) { + const ethers = deploymentManager.hre.ethers; + await deploymentManager.spider(); // Pull in Arbitrum COMP now that reward config has been set + const usdceComet = await deploymentManager.fromDep('usdceComet', 'arbitrum-goerli', 'usdc.e', 'comet'); + const { + comet, + rewards, + } = await deploymentManager.getContracts(); + + const config = await rewards.rewardConfig(comet.address); + + // 1. Verify state changes + const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); + expect(stateChanges).to.deep.equal({ + LINK: { + supplyCap: exp(5_000_000, 18) + }, + WETH: { + supplyCap: exp(5_000, 18) + }, + WBTC: { + supplyCap: exp(300, 8) + }, + baseTrackingSupplySpeed: exp(34.74 / 86400, 15, 18), + baseTrackingBorrowSpeed: exp(34.74 / 86400, 15, 18), + }); + + expect(config.token).to.be.equal(arbitrumCOMPAddress); + expect(config.rescaleFactor).to.be.equal(exp(1, 12)); + expect(config.shouldUpscale).to.be.equal(true); + // Ensure proposal has set usdce market to 0 + expect(await usdceComet.baseTrackingSupplySpeed()).to.be.equal(0); + expect(await usdceComet.baseTrackingBorrowSpeed()).to.be.equal(0); + + // 2. & 3. Verify the seeded USDC reaches Comet reserve + expect(await comet.getReserves()).to.be.equal(exp(10, 6)); + + // 4. Verify the official markets are updated + 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({ + 5: [ + { + baseSymbol: 'USDC', + cometAddress: '0x3EE77595A8459e93C2888b13aDB354017B198188', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x9A539EEc489AAA03D588212a164d0abdB5F08F5F', + }, + ], + + 420: [ + { + baseSymbol: 'USDC', + cometAddress: '0xb8F2f9C84ceD7bBCcc1Db6FB7bb1F19A9a4adfF4' + } + ], + + 421613: [ + { + baseSymbol: 'USDC.e', + cometAddress: '0x1d573274E19174260c5aCE3f2251598959d24456', + }, + { + baseSymbol: 'USDC', + cometAddress: comet.address + }, + ], + + 59140: [ + { + baseSymbol: 'USDC', + cometAddress: '0xa84b24A43ba1890A165f94Ad13d0196E5fD1023a' + } + ], + + 84531: [ + { + baseSymbol: 'USDC', + cometAddress: '0xe78Fc55c884704F9485EDa042fb91BfE16fD55c1' + }, + { + baseSymbol: 'WETH', + cometAddress: '0xED94f3052638620fE226a9661ead6a39C2a265bE' + } + ], + + 80001: [ + { + baseSymbol: 'USDC', + cometAddress: '0xF09F0369aB0a875254fB565E52226c88f10Bc839' + }, + ] + }); + } +}); diff --git a/deployments/arbitrum-goerli/usdc/relations.ts b/deployments/arbitrum-goerli/usdc/relations.ts index 26e0d86c9..8ad1e0e2d 100644 --- a/deployments/arbitrum-goerli/usdc/relations.ts +++ b/deployments/arbitrum-goerli/usdc/relations.ts @@ -8,6 +8,15 @@ export default { ClonableBeaconProxy: { artifact: 'contracts/ERC20.sol:ERC20' }, + // Native USDC + '0xfd064A18f3BF249cf1f87FC203E90D8f650f2d63': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, // WETH '0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3': { artifact: 'contracts/ERC20.sol:ERC20', diff --git a/deployments/arbitrum-goerli/usdc/roots.json b/deployments/arbitrum-goerli/usdc/roots.json index 1ef25d7d2..6f3a59a8c 100644 --- a/deployments/arbitrum-goerli/usdc/roots.json +++ b/deployments/arbitrum-goerli/usdc/roots.json @@ -1,7 +1,8 @@ { - "comet": "0x1d573274E19174260c5aCE3f2251598959d24456", + "comet": "0x0C94d3F9D7F211630EDecAF085718Ac80821A6cA", "configurator": "0x1Ead344570F0f0a0cD86d95d8adDC7855C8723Fb", "rewards": "0x8DA65F8E3Aa22A498211fc4204C498ae9050DAE4", "bridgeReceiver": "0xAC9fC1a9532BC92a9f33eD4c6Ce4A7a54930F376", - "bulker": "0x987350Af5a17b6DdafeB95E6e329c178f44841d7" + "bulker": "0x987350Af5a17b6DdafeB95E6e329c178f44841d7", + "CCTPMessageTransmitter": "0x109bc137cb64eab7c0b1dddd1edf341467dc2d35" } \ No newline at end of file diff --git a/deployments/goerli/usdc/roots.json b/deployments/goerli/usdc/roots.json index 8a08db28f..f693adeb1 100644 --- a/deployments/goerli/usdc/roots.json +++ b/deployments/goerli/usdc/roots.json @@ -12,5 +12,7 @@ "baseL1StandardBridge": "0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a", "lineaMessageService": "0x70BaD09280FD342D02fe64119779BC1f0791BAC2", "lineaL1TokenBridge": "0xaA012D038E6440535Ec66eDf2DA592F4F8398133", - "lineaL1usdcBridge": "0x9c556D2cCfb6157E4A6305aa9963EdD6ca5047cB" + "lineaL1usdcBridge": "0x9c556D2cCfb6157E4A6305aa9963EdD6ca5047cB", + "CCTPTokenMessenger": "0xd0c3da58f55358142b8d3e06c1c30c5c6114efe8", + "CCTPMessageTransmitter": "0x26413e8157cd32011e726065a5462e97dd4d03d9" } \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index e7b9c6bc4..ac2b8f4d1 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -25,7 +25,8 @@ import mainnetRelationConfigMap from './deployments/mainnet/usdc/relations'; import mainnetWethRelationConfigMap from './deployments/mainnet/weth/relations'; import polygonRelationConfigMap from './deployments/polygon/usdc/relations'; import arbitrumRelationConfigMap from './deployments/arbitrum/usdc/relations'; -import arbitrumGoerliRelationConfigMap from './deployments/arbitrum-goerli/usdc/relations'; +import arbitrumBridgedUsdcGoerliRelationConfigMap from './deployments/arbitrum-goerli/usdc.e/relations'; +import arbitrumGoerliNativeUsdcRelationConfigMap from './deployments/arbitrum-goerli/usdc/relations'; import baseGoerliRelationConfigMap from './deployments/base-goerli/usdc/relations'; import baseGoerliWethRelationConfigMap from './deployments/base-goerli/weth/relations'; import lineaGoerliRelationConfigMap from './deployments/linea-goerli/usdc/relations'; @@ -284,7 +285,8 @@ const config: HardhatUserConfig = { usdc: arbitrumRelationConfigMap }, 'arbitrum-goerli': { - usdc: arbitrumGoerliRelationConfigMap + 'usdc.e': arbitrumBridgedUsdcGoerliRelationConfigMap, + usdc: arbitrumGoerliNativeUsdcRelationConfigMap }, 'base-goerli': { usdc: baseGoerliRelationConfigMap, @@ -348,7 +350,13 @@ const config: HardhatUserConfig = { auxiliaryBase: 'mainnet' }, { - name: 'arbitrum-goerli', + name: 'arbitrum-goerli-usdc.e', + network: 'arbitrum-goerli', + deployment: 'usdc.e', + auxiliaryBase: 'goerli' + }, + { + name: 'arbitrum-goerli-usdc', network: 'arbitrum-goerli', deployment: 'usdc', auxiliaryBase: 'goerli' diff --git a/scripts/CCTP-attestation.ts b/scripts/CCTP-attestation.ts new file mode 100644 index 000000000..dd82b0f5b --- /dev/null +++ b/scripts/CCTP-attestation.ts @@ -0,0 +1,65 @@ +/* + A script to help check if CCTP's attestation server to acquire signature to mint native USDC on arbitrum + Example: + DEPLOYMENT=usdc BURN_TXN_HASH= SOURCE_NETWORK=goerli DEST_NETWORK=arbitrum-goerli ETH_PK= npx hardhat run scripts/CCTP-attestation.ts +*/ +import hre from 'hardhat'; +import { DeploymentManager } from '../plugins/deployment_manager/DeploymentManager'; +import { requireEnv } from '../hardhat.config'; + +async function main() { + const DEPLOYMENT = requireEnv('DEPLOYMENT'); + const BURN_TXN_HASH = requireEnv('BURN_TXN_HASH'); + const SOURCE_NETWORK = requireEnv('SOURCE_NETWORK'); + const DEST_NETWORK = requireEnv('DEST_NETWORK'); + await hre.changeNetwork(SOURCE_NETWORK); + const src_dm = new DeploymentManager(SOURCE_NETWORK, DEPLOYMENT, hre, { + writeCacheToDisk: true + }); + + const circleAttestationApiHost = SOURCE_NETWORK === 'mainnet' ? 'https://iris-api.circle.com' : 'https://iris-api-sandbox.circle.com'; + const transactionReceipt = await src_dm.hre.ethers.provider.getTransactionReceipt(BURN_TXN_HASH); + const eventTopic = src_dm.hre.ethers.utils.id('MessageSent(bytes)'); + const log = transactionReceipt.logs.find((l) => l.topics[0] === eventTopic); + const messageBytes = src_dm.hre.ethers.utils.defaultAbiCoder.decode(['bytes'], log.data)[0]; + const messageHash = src_dm.hre.ethers.utils.keccak256(messageBytes); + console.log(`Message hash: ${messageHash}`); + let attestationResponse = { status: 'pending', attestation: ''}; + while (attestationResponse.status != 'complete') { + console.log(`Polling... ${circleAttestationApiHost}/attestations/${messageHash}`); + const response = await fetch(`${circleAttestationApiHost}/attestations/${messageHash}`); + attestationResponse = await response.json(); + console.log(`Response: ${JSON.stringify(attestationResponse)}`); + await new Promise(r => setTimeout(r, 2000)); + } + + console.log(`Attestation complete, proceeding to mint native usdc on ${DEST_NETWORK}:`); + console.log(`------Parameters value------`); + console.log(`receivingMessageBytes: ${messageBytes}`); + console.log(`signature: ${attestationResponse.attestation}`); + console.log(`----------------------------`); + await hre.changeNetwork(DEST_NETWORK); + const dest_dm = new DeploymentManager(DEST_NETWORK, DEPLOYMENT, hre, { + writeCacheToDisk: true + }); + + const CCTPMessageTransmitter = await dest_dm.getContractOrThrow('CCTPMessageTransmitter'); + const signer = await dest_dm.getSigner(); + const transactionRequest = await signer.populateTransaction({ + to: CCTPMessageTransmitter.address, + from: signer.address, + data: CCTPMessageTransmitter.interface.encodeFunctionData('receiveMessage', [messageBytes, attestationResponse.attestation]), + gasPrice: Math.ceil(1.3 * (await hre.ethers.provider.getGasPrice()).toNumber()) + }); + + const mintTxn = await signer.sendTransaction(transactionRequest); + + console.log(`Mint completed, transaction hash: ${mintTxn.hash}`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/src/deploy/index.ts b/src/deploy/index.ts index f63217f5b..df5e2b0df 100644 --- a/src/deploy/index.ts +++ b/src/deploy/index.ts @@ -101,7 +101,8 @@ export const WHALES = { 'arbitrum-goerli': [ '0x4984cbfa5b199e5920995883d345bbe14b005db7', // USDC whale '0xbbfe34e868343e6f4f5e8b5308de980d7bd88c46', // LINK whale - '0x8DA65F8E3Aa22A498211fc4204C498ae9050DAE4' // COMP whale + '0x8DA65F8E3Aa22A498211fc4204C498ae9050DAE4', // COMP whale + '0x6ed0c4addc308bb800096b8daa41de5ae219cd36' // native USDC whale ], 'base-goerli': [ '0x21856935e5689490c72865f34CC665D0FF25664b' // USDC whale diff --git a/tasks/deployment_manager/task.ts b/tasks/deployment_manager/task.ts index a49074384..f68286910 100644 --- a/tasks/deployment_manager/task.ts +++ b/tasks/deployment_manager/task.ts @@ -7,8 +7,8 @@ import { impersonateAddress } from '../../plugins/scenario/utils'; import hreForBase from '../../plugins/scenario/utils/hreForBase'; // TODO: Don't depend on scenario's hreForBase -function getForkEnv(env: HardhatRuntimeEnvironment): HardhatRuntimeEnvironment { - const base = env.config.scenario.bases.find(b => b.name == env.network.name); +function getForkEnv(env: HardhatRuntimeEnvironment, deployment: string): HardhatRuntimeEnvironment { + const base = env.config.scenario.bases.find(b => b.network == env.network.name && b.deployment == deployment); if (!base) { throw new Error(`No fork spec for ${env.network.name}`); } @@ -65,7 +65,7 @@ task('deploy', 'Deploys market') .addFlag('overwrite', 'overwrites cache') .addParam('deployment', 'The deployment to deploy') .setAction(async ({ simulate, noDeploy, noVerify, noVerifyImpl, overwrite, deployment }, env) => { - const maybeForkEnv = simulate ? getForkEnv(env) : env; + const maybeForkEnv = simulate ? getForkEnv(env, deployment) : env; const network = env.network.name; const tag = `${network}/${deployment}`; const dm = new DeploymentManager( @@ -174,7 +174,7 @@ task('migrate', 'Runs migration') .addFlag('overwrite', 'overwrites artifact if exists, fails otherwise') .setAction( async ({ migration: migrationName, prepare, enact, noEnacted, simulate, overwrite, deployment, impersonate }, env) => { - const maybeForkEnv = simulate ? getForkEnv(env) : env; + const maybeForkEnv = simulate ? getForkEnv(env, deployment) : env; const network = env.network.name; const dm = new DeploymentManager( network,