Skip to content

Commit

Permalink
Polygon cUSDCv3 deploy (compound-finance#598)
Browse files Browse the repository at this point in the history
* Polygon cUSDCv3 deploy

delete unused import

Polygon OZ feedback (compound-finance#666)

* consistent GRACE_PERIOD check

* L-03 inconstent usage of uint across loops

* L-04 Lack of indexed parameter

* update bridge receiver test for GRACE_PERIOD check

* change FxChild to IFxMessageProcessor; delete vendoza manifest

* replace messageSender with rootMessageSender

* replace ProposalNotQueued with ProposalNotExecutable

* doc strings for ITimelock.sol

* update additional rootMessageSender instances

* doc strings for PolygonBridgeReceiver

* typo, missing word

* additional messageSender updates

Address incorrectly setting `localTimelock` (compound-finance#665)

* M-01 remove functions to update timelock

* Check that local timelock's admin is the bridge receiver in initialize

* Scenario to test L2 governance contracts upgrade flow

* Remove comment

* Fix lint

* Small refactor

---------

Co-authored-by: kevincheng96 <[email protected]>

Polygon collateral assets (compound-finance#668)

* update assets for polygon

* update deploy script

* WMATIC as alias

* reorder assets

* update bulker scens

* lint

* use WBTC / USD price feed

* supply caps to 0

* bump supply cap for WMATIC

update NativeTokenConstraint; add Polygon whales (compound-finance#670)

Deploy Mumbai (compound-finance#595)

* empty roots.json

* Modified deployment roots from GitHub Actions

* Clear out roots for redeploy

* Modified deployment roots from GitHub Actions

---------

Co-authored-by: GitHub Actions Bot <>
Co-authored-by: kevincheng96 <[email protected]>

Remove unused events from BaseBridgeReceiver

Fix mumbai deploy script and re-deploy (compound-finance#676)

* Modify mumbai deploy script to use polygon WETH and empty roots for re-deploy

* Modified deployment roots from GitHub Actions

* Fix bulker args in deploy script and remove bulker from roots

* Modified deployment roots from GitHub Actions

---------

Co-authored-by: GitHub Actions Bot <>

Polygon Liquidator updates (compound-finance#677)

remove (Polygon) from name (compound-finance#684)

update Polygon timelock delay (compound-finance#682)

* update polygon timelock delay

* HOUR and DAY

Define RewardTokenAddress in polygon configuration.json (compound-finance#683)

* Update roots.json after deploy

* Polygon enable cUSDCv3 proposal (compound-finance#672)

* Polygon supply cap proposal

update enact fn type

update isBridgeProposal definition

update proposal to take both deployment managers; target configurator instead of bridgeReceiver

add fxRoot to roots.json

update getCompWhales

update MigrationConstraint logic to impersonate governanceDM's proposer

update enact call in task

update targetReserves

Set entire Comet configuration (compound-finance#680)

This allows us to update a variety of configs (e.g. CF, supply caps) more easily.

Bridge USDC reserves and COMP rewards to Polygon Comet (compound-finance#681)

This PR adds ERC20 bridging actions to the proposal for bridging USDC reserves and COMP rewards. I also added support for relaying Polygon token bridging events to our scenarios.

Add official markets ENS records to the Polygon proposal (compound-finance#674)

Fix Polygon scenarios (compound-finance#686)

* Fix issue where proposer did not have enough gas

* Actually fix gas issue, which also fixes the unrecognized events issue

* Fix reward scenarios for Comets where the reward token is not a collateral asset

* Lower amount of WBTC to liquidate on Polygon

* Use isBridgedDeployment; fix usages of matchesDeployment

* Use matchesDeployment instead of isBridgedDeployment

* Skip liquidation bot scens for mainnet-weth because we are trying to source too much base assets rn

* Support multiple connections to Seacrest in Enact (compound-finance#692)

Currently, running `Enact` in CI using Seacrest will fail when multiple networks are used (e.g. for cross-chain governance proposals that read from Polygon and write to mainnet). This is because Seacrest's connection with WalletConnect is for a specific chain, so trying to access a different chain will result in a `could not detect network` error.

This change supports making multiple connections to Seacrest/WalletConnect, all from a single phone. We introduce optional `GOV_NETWORK` and `GOV_NETWORK_PROVIDER` env variables to override the provider for a specific chain when creating the HREs. The `Enact` CI job determines the `GOV_NETWORK` based on what the original selected network is.

* Set pause guardian to new Polygon multisig (compound-finance#693)

* Final Gauntlet recommendations and migration of cUSDT rewards (compound-finance#694)

* Final Gauntlet recommendations and migration of cUSDT rewards

* Keep cUSDT borrow speed

* Fix Liquidation Bot scenarios (compound-finance#688)

* update TokenBalanceConstraint to take fn as argument; update Liquidation Bot scenario amounts

* update WETH amount

* Update the proposal text (compound-finance#695)

* Update the proposal text

* Link to public Gauntlet post

* Update token amounts to bridge (compound-finance#697)

* Modified migration from GitHub Actions

* Adapt Polygon proposal to Mumbai (compound-finance#690)

* Adapt Polygon proposal to Mumbai

* Update name of proposal

* Enacted

---------

Co-authored-by: Kevin Cheng <[email protected]>
Co-authored-by: Jared Flatow <[email protected]>
Co-authored-by: GitHub Actions Bot <>

* Pick up incomplete bridged proposals too (compound-finance#707)

---------

Co-authored-by: kevincheng96 <[email protected]>
Co-authored-by: Jared Flatow <[email protected]>
  • Loading branch information
3 people authored Mar 7, 2023
1 parent 92cc65c commit 9ce9aa9
Show file tree
Hide file tree
Showing 51 changed files with 1,910 additions and 496 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/deploy-market.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- mainnet
- goerli
- mumbai
- polygon
deployment:
description: Deployment Name (e.g. "usdc")
required: true
Expand All @@ -32,7 +33,7 @@ jobs:
- name: Seacrest
uses: hayesgm/seacrest@v1
with:
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan.infura.io/v3/$INFURA_KEY\",\"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\"}')[inputs.network] }}"
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan-eth.compound.finance\",\"mainnet\":\"https://mainnet-eth.compound.finance\",\"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\"}')[inputs.network] }}"
port: 8585
if: github.event.inputs.eth_pk == ''

Expand Down
23 changes: 22 additions & 1 deletion .github/workflows/enact-migration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- mainnet
- goerli
- mumbai
- polygon
deployment:
description: Deployment Name (e.g. "usdc")
required: true
Expand All @@ -37,13 +38,31 @@ jobs:
INFURA_KEY: ${{ secrets.INFURA_KEY }}
POLYGONSCAN_KEY: ${{ secrets.POLYGONSCAN_KEY }}
steps:
- name: Get governance network
run: |
case ${{ github.event.inputs.network }} in
polygon)
echo "GOV_NETWORK=mainnet" >> $GITHUB_ENV ;;
mumbai)
echo "GOV_NETWORK=goerli" >> $GITHUB_ENV ;;
*)
echo "No governance network for selected network" ;;
esac
- name: Seacrest
uses: hayesgm/seacrest@v1
with:
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan.infura.io/v3/$INFURA_KEY\",\"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\"}')[inputs.network] }}"
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan.infura.io/v3/$INFURA_KEY\",\"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\"}')[inputs.network] }}"
port: 8585
if: github.event.inputs.eth_pk == ''

- name: Seacrest (governance network)
uses: hayesgm/seacrest@v1
with:
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan.infura.io/v3/$INFURA_KEY\",\"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\"}')[env.GOV_NETWORK] }}"
port: 8685
if: github.event.inputs.eth_pk == '' && env.GOV_NETWORK != ''

- name: Checkout repository
uses: actions/checkout@v2

Expand Down Expand Up @@ -75,6 +94,8 @@ jobs:
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 == ''] }}

- name: Commit changes
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/prepare-migration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- mainnet
- goerli
- mumbai
- polygon
deployment:
description: Deployment Name (e.g. "usdc")
required: true
Expand All @@ -35,7 +36,7 @@ jobs:
- name: Seacrest
uses: hayesgm/seacrest@v1
with:
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan.infura.io/v3/$INFURA_KEY\",\"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\"}')[inputs.network] }}"
ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"kovan\":\"https://kovan.infura.io/v3/$INFURA_KEY\",\"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\"}')[inputs.network] }}"
port: 8585
if: github.event.inputs.eth_pk == ''

Expand Down
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, fuji, mumbai ]
bases: [ development, mainnet, mainnet-weth, goerli, goerli-weth, fuji, mumbai, polygon ]
name: Run scenarios
env:
ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }}
Expand Down
1 change: 0 additions & 1 deletion MIGRATIONS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# Migrations

Migrations are simple scripts which deploy or modify contracts. The goal of migration scripts is to make sure that users can see potential changes that are run prior to creating a governance proposal. This is a "nothing up my sleeve" approach to governance preparation (as in, the magician rolls up his sleeves to show there's nothing there-- so the developer deploys scripts from GitHub to show which code was deployed or run).
Expand Down
6 changes: 6 additions & 0 deletions contracts/IGovernorBravo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,10 @@ interface IGovernorBravo {
function queue(uint256 proposalId) external payable;
function execute(uint256 proposalId) external payable;
function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);
function getActions(uint proposalId) external view returns (
address[] memory targets,
uint[] memory values,
string[] memory signatures,
bytes[] memory calldatas
);
}
64 changes: 64 additions & 0 deletions contracts/ITimelock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,91 @@ pragma solidity 0.8.15;
* @dev Interface for interacting with a Timelock
*/
interface ITimelock {
/// @notice Event emitted when a pending admin accepts admin position
event NewAdmin(address indexed newAdmin);

/// @notice Event emitted when new pending admin is set by the timelock
event NewPendingAdmin(address indexed newPendingAdmin);

/// @notice Event emitted when Timelock sets new delay value
event NewDelay(uint indexed newDelay);

/// @notice Event emitted when admin cancels an enqueued transaction
event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);

/// @notice Event emitted when admin executes an enqueued transaction
event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);

/// @notice Event emitted when admin enqueues a transaction
event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta);

/// @notice The length of time, once the delay has passed, in which a transaction can be executed before it becomes stale
function GRACE_PERIOD() virtual external view returns (uint);

/// @notice The minimum value that the `delay` variable can be set to
function MINIMUM_DELAY() virtual external view returns (uint);

/// @notice The maximum value that the `delay` variable can be set to
function MAXIMUM_DELAY() virtual external view returns (uint);

/// @notice Address that has admin privileges
function admin() virtual external view returns (address);

/// @notice The address that may become the new admin by calling `acceptAdmin()`
function pendingAdmin() virtual external view returns (address);

/**
* @notice Set the pending admin
* @param pendingAdmin_ New pending admin address
*/
function setPendingAdmin(address pendingAdmin_) virtual external;

/**
* @notice Accept the position of admin (if caller is the current pendingAdmin)
*/
function acceptAdmin() virtual external;

/// @notice Duration that a transaction must be queued before it can be executed
function delay() virtual external view returns (uint);

/**
* @notice Set the delay value
* @param delay New delay value
*/
function setDelay(uint delay) virtual external;

/// @notice Mapping of transaction hashes to whether that transaction is currently enqueued
function queuedTransactions(bytes32 txHash) virtual external returns (bool);

/**
* @notice Enque a transaction
* @param target Address that the transaction is targeted at
* @param value Value to send to target address
* @param signature Function signature to call on target address
* @param data Calldata for function called on target address
* @param eta Timestamp of when the transaction can be executed
* @return txHash of the enqueued transaction
*/
function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) virtual external returns (bytes32);

/**
* @notice Cancel an enqueued transaction
* @param target Address that the transaction is targeted at
* @param value Value of the transaction to cancel
* @param signature Function signature of the transaction to cancel
* @param data Calldata for the transaction to cancel
* @param eta Timestamp of the transaction to cancel
*/
function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) virtual external;

/**
* @notice Execute an enqueued transaction
* @param target Target address of the transaction to execute
* @param value Value of the transaction to execute
* @param signature Function signature of the transaction to execute
* @param data Calldata for the transaction to execute
* @param eta Timestamp of the transaction to execute
* @return bytes returned from executing transaction
*/
function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) virtual external payable returns (bytes memory);
}
55 changes: 12 additions & 43 deletions contracts/bridges/BaseBridgeReceiver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@ contract BaseBridgeReceiver {
error AlreadyInitialized();
error BadData();
error InvalidProposalId();
error ProposalNotQueued();
error InvalidTimelockAdmin();
error ProposalNotExecutable();
error TransactionAlreadyQueued();
error Unauthorized();

/** Events **/
event Initialized(address indexed govTimelock, address indexed localTimelock);
event NewLocalTimelock(address indexed oldLocalTimelock, address indexed newLocalTimelock);
event NewGovTimelock(address indexed oldGovTimelock, address indexed newGovTimelock);
event ProposalCreated(address indexed messageSender, uint id, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint eta);
event ProposalExecuted(uint id);
event ProposalCreated(address indexed rootMessageSender, uint id, address[] targets, uint[] values, string[] signatures, bytes[] calldatas, uint eta);
event ProposalExecuted(uint indexed id);

/** Public variables **/

Expand Down Expand Up @@ -63,52 +62,23 @@ contract BaseBridgeReceiver {
*/
function initialize(address _govTimelock, address _localTimelock) external {
if (initialized) revert AlreadyInitialized();
if (ITimelock(_localTimelock).admin() != address(this)) revert InvalidTimelockAdmin();
govTimelock = _govTimelock;
localTimelock = _localTimelock;
initialized = true;
emit Initialized(_govTimelock, _localTimelock);
}

/**
* @notice Accept admin role for the localTimelock
*/
function acceptLocalTimelockAdmin() external {
if (msg.sender != localTimelock) revert Unauthorized();
ITimelock(localTimelock).acceptAdmin();
}

/**
* @notice Set localTimelock address
* @param newTimelock Address to set as the localTimelock
*/
function setLocalTimelock(address newTimelock) public {
if (msg.sender != localTimelock) revert Unauthorized();
address oldLocalTimelock = localTimelock;
localTimelock = newTimelock;
emit NewLocalTimelock(oldLocalTimelock, newTimelock);
}

/**
* @notice Set govTimelock address
* @param newTimelock Address to set as the govTimelock
*/
function setGovTimelock(address newTimelock) public {
if (msg.sender != localTimelock) revert Unauthorized();
address oldGovTimelock = govTimelock;
govTimelock = newTimelock;
emit NewGovTimelock(oldGovTimelock, newTimelock);
}

/**
* @notice Process a message sent from the governing timelock (across a bridge)
* @param messageSender Address of the contract that sent the bridged message
* @param rootMessageSender Address of the contract that sent the bridged message
* @param data ABI-encoded bytes containing the transactions to be queued on the local timelock
*/
function processMessage(
address messageSender,
address rootMessageSender,
bytes calldata data
) internal {
if (messageSender != govTimelock) revert Unauthorized();
if (rootMessageSender != govTimelock) revert Unauthorized();

address[] memory targets;
uint256[] memory values;
Expand All @@ -127,10 +97,9 @@ contract BaseBridgeReceiver {
uint delay = ITimelock(localTimelock).delay();
uint eta = block.timestamp + delay;

for (uint8 i = 0; i < targets.length; ) {
for (uint i = 0; i < targets.length; i++) {
if (ITimelock(localTimelock).queuedTransactions(keccak256(abi.encode(targets[i], values[i], signatures[i], calldatas[i], eta)))) revert TransactionAlreadyQueued();
ITimelock(localTimelock).queueTransaction(targets[i], values[i], signatures[i], calldatas[i], eta);
unchecked { i++; }
}

proposalCount++;
Expand All @@ -145,15 +114,15 @@ contract BaseBridgeReceiver {
});

proposals[proposal.id] = proposal;
emit ProposalCreated(messageSender, proposal.id, targets, values, signatures, calldatas, eta);
emit ProposalCreated(rootMessageSender, proposal.id, targets, values, signatures, calldatas, eta);
}

/**
* @notice Execute a queued proposal
* @param proposalId The id of the proposal to execute
*/
function executeProposal(uint proposalId) external {
if (state(proposalId) != ProposalState.Queued) revert ProposalNotQueued();
if (state(proposalId) != ProposalState.Queued) revert ProposalNotExecutable();
Proposal storage proposal = proposals[proposalId];
proposal.executed = true;
for (uint i = 0; i < proposal.targets.length; i++) {
Expand All @@ -172,7 +141,7 @@ contract BaseBridgeReceiver {
Proposal memory proposal = proposals[proposalId];
if (proposal.executed) {
return ProposalState.Executed;
} else if (block.timestamp >= (proposal.eta + ITimelock(localTimelock).GRACE_PERIOD())) {
} else if (block.timestamp > (proposal.eta + ITimelock(localTimelock).GRACE_PERIOD())) {
return ProposalState.Expired;
} else {
return ProposalState.Queued;
Expand Down
21 changes: 18 additions & 3 deletions contracts/bridges/polygon/PolygonBridgeReceiver.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,48 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.15;

import "../vendor/fx-portal/contracts/FxChild.sol";
import "./IFxMessageProcessor.sol";
import "../BaseBridgeReceiver.sol";

contract PolygonBridgeReceiver is IFxMessageProcessor, BaseBridgeReceiver {
error InvalidChild();

event NewFxChild(address indexed oldFxChild, address indexed newFxChild);

/// @notice Address of Polygon's bridged message receiver
address public fxChild;

/**
* @notice Construct a new PolygonBridgeReceiver instance
* @param _fxChild Address of Polygon bridged message receiver
**/
constructor(address _fxChild) {
fxChild = _fxChild;
}

/**
* @notice Update the fxChild address
* @param newFxChild New value for fxAddress
*/
function changeFxChild(address newFxChild) public {
if (msg.sender != localTimelock) revert Unauthorized();
address oldFxChild = fxChild;
fxChild = newFxChild;
emit NewFxChild(oldFxChild, newFxChild);
}

/**
* @notice Receive bridged message and enqueue in the Timelock
* @param stateId Value provided by fxChild when function is called; ignored
* @param rootMessageSender Mainnet address that initiated the bridged message
* @param data ABI-encoded data of the bridged message
*/
function processMessageFromRoot(
uint256 stateId,
address messageSender,
address rootMessageSender,
bytes calldata data
) public override {
if (msg.sender != fxChild) revert InvalidChild();
processMessage(messageSender, data);
processMessage(rootMessageSender, data);
}
}
4 changes: 2 additions & 2 deletions contracts/bridges/test/BaseBridgeReceiverHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import "../BaseBridgeReceiver.sol";

contract BaseBridgeReceiverHarness is BaseBridgeReceiver {
function processMessageExternal(
address messageSender,
address rootMessageSender,
bytes calldata data
) external {
processMessage(messageSender, data);
processMessage(rootMessageSender, data);
}
}
Loading

0 comments on commit 9ce9aa9

Please sign in to comment.