Skip to content

Commit

Permalink
Migrate Arbitrum Goerli Native usdc (#779)
Browse files Browse the repository at this point in the history
* 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 <kevincheng96@hotmail.com>
Co-authored-by: GitHub Actions Bot <>
  • Loading branch information
cwang25 and kevincheng96 committed Jul 18, 2023
1 parent 55c38eb commit f91789b
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 14 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/enact-migration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,19 @@ jobs:
esac
- name: Seacrest
uses: hayesgm/seacrest@v1
uses: hayesgm/seacrest@v2
with:
wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }}
requested_network: "${{ inputs.network }}"
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\"}')[inputs.network] }}"
port: 8585
if: github.event.inputs.eth_pk == ''

- name: Seacrest (governance network)
uses: hayesgm/seacrest@v1
uses: hayesgm/seacrest@v2
with:
wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }}
requested_network: "${{ env.GOV_NETWORK }}"
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\"}')[env.GOV_NETWORK] }}"
port: 8685
if: github.event.inputs.eth_pk == '' && env.GOV_NETWORK != ''
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/prepare-migration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ jobs:
LINEASCAN_KEY: ${{ secrets.LINEASCAN_KEY }}
steps:
- name: Seacrest
uses: hayesgm/seacrest@v1
uses: hayesgm/seacrest@v2
with:
wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }}
requested_network: "${{ inputs.network }}"
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\"}')[inputs.network] }}"
port: 8585
if: github.event.inputs.eth_pk == ''
Expand Down
10 changes: 5 additions & 5 deletions deployments/arbitrum-goerli/usdc/configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
},
"tracking": {
"indexScale": "1e15",
"baseSupplySpeed": "0e15",
"baseBorrowSpeed": "0e15",
"baseSupplySpeed": "0.000402083333333e15",
"baseBorrowSpeed": "0.000402083333333e15",
"baseMinForRewards": "10000e6"
},
"assets": {
Expand All @@ -32,7 +32,7 @@
"borrowCF": 0.775,
"liquidateCF": 0.825,
"liquidationFactor": 0.95,
"supplyCap": "0e18"
"supplyCap": "5000000e18"
},
"WETH": {
"address": "0xe39ab88f8a4777030a534146a9ca3b52bd5d43a3",
Expand All @@ -41,7 +41,7 @@
"borrowCF": 0.775,
"liquidateCF": 0.825,
"liquidationFactor": 0.95,
"supplyCap": "0e18"
"supplyCap": "5000e18"
},
"WBTC": {
"address": "0x22d5e2dE578677791f6c90e0110Ec629be9d5Fb5",
Expand All @@ -50,7 +50,7 @@
"borrowCF": 0.7,
"liquidateCF": 0.75,
"liquidationFactor": 0.93,
"supplyCap": "0e8"
"supplyCap": "300e8"
}
}
}
Original file line number Diff line number Diff line change
@@ -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<boolean> {
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'
},
]
});
}
});
3 changes: 2 additions & 1 deletion deployments/arbitrum-goerli/usdc/roots.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"configurator": "0x1Ead344570F0f0a0cD86d95d8adDC7855C8723Fb",
"rewards": "0x8DA65F8E3Aa22A498211fc4204C498ae9050DAE4",
"bridgeReceiver": "0xAC9fC1a9532BC92a9f33eD4c6Ce4A7a54930F376",
"bulker": "0x987350Af5a17b6DdafeB95E6e329c178f44841d7"
"bulker": "0x987350Af5a17b6DdafeB95E6e329c178f44841d7",
"CCTPMessageTransmitter": "0x109bc137cb64eab7c0b1dddd1edf341467dc2d35"
}
11 changes: 6 additions & 5 deletions deployments/goerli/usdc/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ export default {
}
},
lineaMessageService: {
delegates: {
field: {
slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'
}
}
artifact: 'contracts/bridges/linea/IMessageService.sol:IMessageService',
// delegates: {
// field: {
// slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc'
// }
// }
},
lineaL1TokenBridge: {
delegates: {
Expand Down
Loading

0 comments on commit f91789b

Please sign in to comment.