Skip to content

Commit

Permalink
Create native USDC market on Base (compound-finance#828)
Browse files Browse the repository at this point in the history
  • Loading branch information
arr00 authored Mar 19, 2024
1 parent 55960cd commit 56b32ab
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-scenarios.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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-goerli, base-goerli-weth, linea-goerli]
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]
name: Run scenarios
env:
ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }}
Expand Down
47 changes: 47 additions & 0 deletions deployments/base/usdc/configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "Compound USDC",
"symbol": "cUSDCv3",
"baseToken": "USDC",
"baseTokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"baseTokenPriceFeed": "0x7e860098F58bBFC8648a4311b374B1D669a2bc6B",
"borrowMin": "1e0",
"pauseGuardian": "0x3cb4653f3b45f448d9100b118b75a1503281d2ee",
"storeFrontPriceFactor": 0.6,
"targetReserves": "5000000e6",
"rates": {
"supplyKink": 0.85,
"supplySlopeLow": 0.048,
"supplySlopeHigh": 1.6,
"supplyBase": 0,
"borrowKink": 0.85,
"borrowSlopeLow": 0.053,
"borrowSlopeHigh": 1.8,
"borrowBase": 0.015
},
"tracking": {
"indexScale": "1e15",
"baseSupplySpeed": "231_481_481_481e0",
"baseBorrowSpeed": "92_592_592_592e0",
"baseMinForRewards": "1000e6"
},
"assets": {
"cbETH": {
"address": "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22",
"priceFeed": "0x4687670f5f01716fAA382E2356C103BaD776752C",
"decimals": "18",
"borrowCF": 0.75,
"liquidateCF": 0.8,
"liquidationFactor": 0.85,
"supplyCap": "7500e18"
},
"WETH": {
"address": "0x4200000000000000000000000000000000000006",
"priceFeed": "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70",
"decimals": "18",
"borrowCF": 0.80,
"liquidateCF": 0.90,
"liquidationFactor": 0.95,
"supplyCap": "11000e18"
}
}
}
42 changes: 42 additions & 0 deletions deployments/base/usdc/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager';
import { DeploySpec, deployComet } from '../../../src/deploy';

const HOUR = 60 * 60;
const DAY = 24 * HOUR;

const MAINNET_TIMELOCK = '0x6d903f6003cca6255d85cca4d3b5e5146dc33925';

export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise<Deployed> {
const deployed = await deployContracts(deploymentManager, deploySpec);
return deployed;
}

async function deployContracts(
deploymentManager: DeploymentManager,
deploySpec: DeploySpec
): Promise<Deployed> {
// Pull in existing assets
const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'base', 'usdbc');
const cometFactory = await deploymentManager.fromDep('cometFactory', 'base', 'usdbc');
const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'base', 'usdbc');
const configurator = await deploymentManager.fromDep('configurator', 'base', 'usdbc');
const rewards = await deploymentManager.fromDep('rewards', 'base', 'usdbc');
const bulker = await deploymentManager.fromDep('bulker', 'base', 'usdbc');
const l2CrossDomainMessenger = await deploymentManager.fromDep('l2CrossDomainMessenger', 'base', 'usdbc');
const l2StandardBridge = await deploymentManager.fromDep('l2StandardBridge', 'base', 'usdbc');
const localTimelock = await deploymentManager.fromDep('timelock', 'base', 'usdbc');
const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'base', 'usdbc');
const cbETHMultiplicativePriceFeed = await deploymentManager.fromDep('cbETH:priceFeed','base', 'usdbc');


// Deploy Comet
const deployed = await deployComet(deploymentManager, deploySpec);

return {
...deployed,
bridgeReceiver,
l2CrossDomainMessenger, // TODO: don't have to part of roots. can be pulled via relations
l2StandardBridge,
bulker,
};
}
205 changes: 205 additions & 0 deletions deployments/base/usdc/migrations/1689892563_configurate_and_ens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
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 { expect } from 'chai';

const ENSName = 'compound-community-licenses.eth';
const ENSResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41';
const ENSSubdomainLabel = 'v3-additional-grants';
const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`;
const ENSTextRecordKey = 'v3-official-markets';
const baseCOMPAddress = '0x9e1028F5F1D5eDE59748FFceE5532509976840E0';

export default migration('1689892563_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', 'base', 'usdbc');
const {
bridgeReceiver,
comet,
cometAdmin,
configurator,
rewards
} = await deploymentManager.getContracts();

const {
baseL1CrossDomainMessenger,
governor,
COMP: mainnetCOMP
} = 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 baseChainId = (await deploymentManager.hre.ethers.provider.getNetwork()).chainId.toString();
const newMarketObject = { baseSymbol: 'USDC', cometAddress: comet.address };
const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey));
if (officialMarketsJSON[baseChainId]) {
officialMarketsJSON[baseChainId].push(newMarketObject);
} else {
officialMarketsJSON[baseChainId] = [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, baseCOMPAddress]
);
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 actions = [
// 1. Set Comet configuration + deployAndUpgradeTo new Comet and set reward config on Base.
{
contract: baseL1CrossDomainMessenger,
signature: 'sendMessage(address,bytes,uint32)',
args: [bridgeReceiver.address, l2ProposalData, 2_500_000]
},

// 2. 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)]
)
},

// 3. Send 108 COMP to arr00
{
contract: mainnetCOMP,
signature: 'transfer(address,uint256)',
args: ['0x2B384212EDc04Ae8bB41738D05BA20E33277bf33', exp(115, 18)]
}
];

const description = '# Initialize cUSDCv3 on Base\n\nThis proposal initializes the Compound III USDC market on Base as a first step to fully migrating to native USDC on base. The entirety of this proposal was simulated and indicates success. The [parameters for this deployment](https://www.comp.xyz/t/gauntlet-usdc-native-comet-market-to-compound-base-02-09-24/4982) and the [rewards configuration](https://www.comp.xyz/t/gauntlet-native-usdc-base-comet-incentives-recommendations/5009) were recommended by Gauntlet.\n\nSee links for more details:\n- [proposal pull request](https://github.com/compound-finance/comet/pull/828)\n- [forum discussion](https://www.comp.xyz/t/gauntlet-usdc-native-comet-market-to-compound-base-02-09-24/4982)\n\n## Proposal Actions\n1) The first proposal action sets the configurator factory address, sets Comet configuration, and deploys a new Comet implementation on Base. It also sets the rewards config for the new market to 20 COMP/Day for suppliers and 8 COMP/Day for borrowers.\n2) The second 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 Base cUSDCv3 market.\n3) The third action pays arr00 115 COMP for the development effort for this proposal.';
const txn = await govDeploymentManager.retry(async () =>
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<boolean> {
return true;
},

async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) {
const ethers = deploymentManager.hre.ethers;
await deploymentManager.spider(); // We spider here to pull in Base COMP now that reward config has been set

const {
comet,
rewards,
COMP
} = await deploymentManager.getContracts();

// 1.
const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber);
expect(stateChanges).to.deep.equal({
baseTrackingSupplySpeed: exp(20 / 86400, 15, 18),
baseTrackingBorrowSpeed: exp(8 / 86400, 15, 18),
WETH: {
supplyCap: exp(11000, 18)
},
cbETH: {
supplyCap: exp(7500, 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.
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',
},
],
42161: [
{
baseSymbol: 'USDC.e',
cometAddress: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA',
},
{
baseSymbol: 'USDC',
cometAddress:'0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf',
}
],
8453: [
{
baseSymbol: 'USDbC',
cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf',
},
{
baseSymbol: 'WETH',
cometAddress: '0x46e6b214b524310239732D51387075E0e70970bf',
},
{
baseSymbol: 'USDC',
cometAddress: comet.address,
}
],
});

// 20 comp per day to suppliers
expect(await comet.baseTrackingSupplySpeed()).to.be.equal(exp(20 / 86400, 15, 18));
// 8 comp per day to borrowers
expect(await comet.baseTrackingBorrowSpeed()).to.be.equal(exp(8 / 86400, 15, 18));
}
});
28 changes: 28 additions & 0 deletions deployments/base/usdc/relations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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'
}
}
}
};
9 changes: 9 additions & 0 deletions deployments/base/usdc/roots.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"comet": "0xb125E6687d4313864e53df431d5425969c15Eb2F",
"configurator": "0x45939657d1CA34A8FA39A924B71D28Fe8431e581",
"rewards": "0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1",
"bridgeReceiver": "0x18281dfC4d00905DA1aaA6731414EABa843c468A",
"l2CrossDomainMessenger": "0x4200000000000000000000000000000000000007",
"l2StandardBridge": "0x4200000000000000000000000000000000000010",
"bulker": "0x78D0677032A35c63D142a48A2037048871212a8C"
}
10 changes: 9 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import arbitrumBridgedUsdcGoerliRelationConfigMap from './deployments/arbitrum-g
import arbitrumGoerliNativeUsdcRelationConfigMap from './deployments/arbitrum-goerli/usdc/relations';
import baseUsdbcRelationConfigMap from './deployments/base/usdbc/relations';
import baseWethRelationConfigMap from './deployments/base/weth/relations';
import baseUsdcRelationConfigMap from './deployments/base/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';
Expand Down Expand Up @@ -319,7 +320,8 @@ const config: HardhatUserConfig = {
},
'base': {
usdbc: baseUsdbcRelationConfigMap,
weth: baseWethRelationConfigMap
weth: baseWethRelationConfigMap,
usdc: baseUsdcRelationConfigMap
},
'base-goerli': {
usdc: baseGoerliRelationConfigMap,
Expand Down Expand Up @@ -422,6 +424,12 @@ const config: HardhatUserConfig = {
deployment: 'weth',
auxiliaryBase: 'mainnet'
},
{
name: 'base-usdc',
network: 'base',
deployment: 'usdc',
auxiliaryBase: 'mainnet'
},
{
name: 'base-goerli',
network: 'base-goerli',
Expand Down

0 comments on commit 56b32ab

Please sign in to comment.