Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add scroll support #17

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

# Aave Governance Cross-Chain Bridges

This repository contains smart contracts and related code for Aave cross-chain bridge executors. This is intended to extend Aave Governance on Ethereum to other networks. This repository currently contains contracts to support bridging to Polygon, Arbitrum and Optimism.
This repository contains smart contracts and related code for Aave cross-chain bridge executors. This is intended to extend Aave Governance on Ethereum to other networks. This repository currently contains contracts to support bridging to Polygon, Arbitrum, Optimism and Scroll.

The core contract is the `BridgeExecutorBase`, an abstract contract that contains the logic to facilitate the queueing, delay, and execution of sets of actions on downstream networks. This base contract needs to be extended with the functionality required for cross-chain transactions on a specific downstream network.

Expand Down Expand Up @@ -225,6 +225,14 @@ Therefore, the `msg.sender` of the cross-chain transaction on Optimism is the OV
- `maximumDelay` - maximum allowed delay
- `guardian` - the admin address of this contract with the permission to cancel ActionsSets

## Scroll Governance Bridge

### Scroll Governance Bridge Architecture

### Scroll Bridge Contracts Functionality

### Deploying the ScrollBridgeExecutor

## License

[BSD-3-Clause](./LICENSE.md)
58 changes: 58 additions & 0 deletions contracts/bridges/ScrollBridgeExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.10;

import {IScrollMessenger} from '../dependencies/scroll/interfaces/IScrollMessenger.sol';
import {L2BridgeExecutor} from './L2BridgeExecutor.sol';

/**
* @title ScrollBridgeExecutor
* @author Aave
zimpha marked this conversation as resolved.
Show resolved Hide resolved
* @notice Implementation of the Scroll Bridge Executor, able to receive cross-chain transactions from Ethereum
* @dev Queuing an ActionsSet into this Executor can only be done by the `L2ScrollMessenger` and having
* the EthereumGovernanceExecutor as xDomainMessageSender
*/
contract ScrollBridgeExecutor is L2BridgeExecutor {
// Address of the `L2ScrollMessenger` contract, in charge of redirecting cross-chain transactions in L2
address public immutable L2_SCROLL_MESSENGER;

/// @inheritdoc L2BridgeExecutor
modifier onlyEthereumGovernanceExecutor() override {
if (
msg.sender != L2_SCROLL_MESSENGER ||
IScrollMessenger(L2_SCROLL_MESSENGER).xDomainMessageSender() != _ethereumGovernanceExecutor
) revert UnauthorizedEthereumExecutor();
_;
}

/**
* @dev Constructor
*
* @param l2ScrollMessenger The address of the `L2ScrollMessenger` contract.
* @param ethereumGovernanceExecutor The address of the `EthereumGovernanceExecutor` contract.
* @param delay The delay before which an actions set can be executed
* @param gracePeriod The time period after a delay during which an actions set can be executed
* @param minimumDelay The minimum bound a delay can be set to
* @param maximumDelay The maximum bound a delay can be set to
* @param guardian The address of the guardian, which can cancel queued proposals (can be zero)
*/
constructor(
address l2ScrollMessenger,
address ethereumGovernanceExecutor,
uint256 delay,
uint256 gracePeriod,
uint256 minimumDelay,
uint256 maximumDelay,
address guardian
)
L2BridgeExecutor(
ethereumGovernanceExecutor,
delay,
gracePeriod,
minimumDelay,
maximumDelay,
guardian
)
{
L2_SCROLL_MESSENGER = l2ScrollMessenger;
}
}
55 changes: 55 additions & 0 deletions contracts/dependencies/scroll/interfaces/IScrollMessenger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;

interface IScrollMessenger {
/**********
* Events *
**********/

/// @notice Emitted when a cross domain message is sent.
/// @param sender The address of the sender who initiates the message.
/// @param target The address of target contract to call.
/// @param value The amount of value passed to the target contract.
/// @param messageNonce The nonce of the message.
/// @param gasLimit The optional gas limit passed to L1 or L2.
/// @param message The calldata passed to the target contract.
event SentMessage(
address indexed sender,
address indexed target,
uint256 value,
uint256 messageNonce,
uint256 gasLimit,
bytes message
);

/// @notice Emitted when a cross domain message is relayed successfully.
/// @param messageHash The hash of the message.
event RelayedMessage(bytes32 indexed messageHash);

/// @notice Emitted when a cross domain message is failed to relay.
/// @param messageHash The hash of the message.
event FailedRelayedMessage(bytes32 indexed messageHash);

/*************************
* Public View Functions *
*************************/

/// @notice Return the sender of a cross domain message.
function xDomainMessageSender() external view returns (address);

/*****************************
* Public Mutating Functions *
*****************************/

/// @notice Send cross chain message from L1 to L2 or L2 to L1.
/// @param target The address of account who receive the message.
/// @param value The amount of ether passed when call target contract.
/// @param message The content of the message.
/// @param gasLimit Gas limit required to complete the message relay on corresponding chain.
function sendMessage(
address target,
uint256 value,
bytes calldata message,
uint256 gasLimit
) external payable;
}
42 changes: 42 additions & 0 deletions contracts/mocks/MockL1ScrollMessenger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//SPDX-License-Identifier: Unlicense
pragma solidity 0.8.10;

import {IScrollMessenger} from '../dependencies/scroll/interfaces/IScrollMessenger.sol';

import {MockL2ScrollMessenger} from "./MockL2ScrollMessenger.sol";

contract MockL1ScrollMessenger is IScrollMessenger {
address private sender;
address private l2Messenger;

function setSender(address _sender) external {
sender = _sender;
}

function setL2Messenger(address _l2Messenger) external {
l2Messenger = _l2Messenger;
}

function xDomainMessageSender() external view override returns (address) {
return sender;
}

function sendMessage(
address _target,
uint256 _value,
bytes calldata _message,
uint256 _gasLimit
) external payable override {
MockL2ScrollMessenger(l2Messenger).redirect{value: _value}(msg.sender, _target, _value, _message, _gasLimit);
}

function redirect(
address _target,
uint256 _value,
bytes calldata _message,
uint256 _gasLimit
) external payable {
bool success;
(success, ) = _target.call{value: _value, gas: _gasLimit}(_message);
}
}
51 changes: 51 additions & 0 deletions contracts/mocks/MockL2ScrollMessenger.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//SPDX-License-Identifier: Unlicense
pragma solidity 0.8.10;

import {IScrollMessenger} from '../dependencies/scroll/interfaces/IScrollMessenger.sol';

import {MockL1ScrollMessenger} from "./MockL1ScrollMessenger.sol";

contract MockL2ScrollMessenger is IScrollMessenger {
address private sender;
address private l1Messenger;

function setSender(address _sender) external {
sender = _sender;
}

function setL1Messenger(address _l1Messenger) external {
l1Messenger = _l1Messenger;
}

function xDomainMessageSender() external view override returns (address) {
return sender;
}

function sendMessage(
address _target,
uint256 _value,
bytes calldata _message,
uint256 _gasLimit
) external payable override {
MockL1ScrollMessenger(l1Messenger).redirect{value:_value}(_target, _value, _message, _gasLimit);
}

// This error must be defined here or else Hardhat will not recognize the selector
error UnauthorizedEthereumExecutor();

function redirect(
address _xDomainMessageSender,
address _target,
uint256 _value,
bytes calldata _message,
uint256 _gasLimit
) external payable {
sender = _xDomainMessageSender;
(bool success, bytes memory data) = _target.call{value: _value, gas: _gasLimit}(_message);
if (!success) {
assembly {
revert(add(data, 32), mload(data))
}
}
}
}
44 changes: 44 additions & 0 deletions deploy/gov-bridge-scroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { DeployFunction } from 'hardhat-deploy/types';
import { ADDRESSES, CONSTANTS } from '../helpers/gov-constants';
import { eScrollNetwork } from '../helpers/types';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments, getNamedAccounts } = hre;
const { deploy, log } = deployments;
const { deployer } = await getNamedAccounts();

log(`Deployer: ${deployer}\n`);

const scrollGov = await deployments.getOrNull('ScrollGov');

if (scrollGov) {
log(`Reusing scroll governance at: ${scrollGov.address}`);
} else {
let L2_SCROLL_MESSENGER = ADDRESSES['L2_SCROLL_MESSENGER'];
let SCROLL_GOV_EXECUTOR = ADDRESSES['SCROLL_GOV_EXECUTOR'];
if (hre.network.name == eScrollNetwork.scrollSepolia) {
L2_SCROLL_MESSENGER = ADDRESSES['L2_SCROLL_MESSENGER_SEPOLIA'];
SCROLL_GOV_EXECUTOR = ADDRESSES['SCROLL_GOV_EXECUTOR_SEPOLIA'];
}

await deploy('ScrollGov', {
args: [
ADDRESSES['L2_SCROLL_MESSENGER'],
ADDRESSES['SCROLL_GOV_EXECUTOR'],
CONSTANTS['DELAY'],
CONSTANTS['GRACE_PERIOD'],
CONSTANTS['MIN_DELAY'],
CONSTANTS['MAX_DELAY'],
ADDRESSES['SCROLL_GUARDIAN'],
],
contract: 'ScrollBridgeExecutor',
from: deployer,
log: true,
});
}
};

export default func;
func.dependencies = [];
func.tags = ['ScrollGov'];
22 changes: 22 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
eNetwork,
eOptimismNetwork,
ePolygonNetwork,
eScrollNetwork,
eXDaiNetwork,
} from './helpers/types';
import { NETWORKS_RPC_URL } from './helper-hardhat-config';
Expand All @@ -46,6 +47,7 @@ const MAINNET_FORK = process.env.MAINNET_FORK === 'true';
const FORKING_BLOCK_NUMBER = process.env.FORKING_BLOCK_NUMBER;
const ARBISCAN_KEY = process.env.ARBISCAN_KEY || '';
const OPTIMISTIC_ETHERSCAN_KEY = process.env.OPTIMISTIC_ETHERSCAN_KEY || '';
const SCROLLSCAN_KEY = process.env.SCROLLSCAN_KEY || '';
const TENDERLY_PROJECT = process.env.TENDERLY_PROJECT || '';
const TENDERLY_USERNAME = process.env.TENDERLY_USERNAME || '';

Expand Down Expand Up @@ -111,7 +113,27 @@ const hardhatConfig: HardhatUserConfig = {
apiKey: {
optimisticEthereum: OPTIMISTIC_ETHERSCAN_KEY,
arbitrumOne: ARBISCAN_KEY,
[eScrollNetwork.scroll]: SCROLLSCAN_KEY,
[eScrollNetwork.scrollSepolia]: SCROLLSCAN_KEY,
},
customChains: [
{
network: eScrollNetwork.scroll,
chainId: 59140,
urls: {
apiURL: 'https://api-sepolia.scrollscan.com/api',
browserURL: 'https://sepolia.scrollscan.com/',
},
},
{
network: eScrollNetwork.scrollSepolia,
chainId: 59144,
urls: {
apiURL: 'https://api.scrollscan.com/api',
browserURL: 'https://scrollscan.com/',
},
},
],
},
tenderly: {
project: TENDERLY_PROJECT,
Expand Down
3 changes: 3 additions & 0 deletions helper-hardhat-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
eOptimismNetwork,
ePolygonNetwork,
eXDaiNetwork,
eScrollNetwork,
iParamsPerNetwork,
} from './helpers/types';

Expand Down Expand Up @@ -37,4 +38,6 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork<string> = {
[eArbitrumNetwork.arbitrumTestnet]: `https://rinkeby.arbitrum.io/rpc`,
[eOptimismNetwork.main]: `https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`,
[eOptimismNetwork.testnet]: `https://opt-kovan.g.alchemy.com/v2/${ALCHEMY_KEY}`,
[eScrollNetwork.scroll]: "https://rpc.scroll.io",
[eScrollNetwork.scrollSepolia]: "https://sepolia-rpc.scroll.io",
};
7 changes: 7 additions & 0 deletions helpers/gov-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ export const ADDRESSES = {
OVM_GUARDIAN: '0x0000000000000000000000000000000000000000',
ARB_GUARDIAN: '0x0000000000000000000000000000000000000000',
RETRYABLE_TICKET_TX_ADDRESS: '0x000000000000000000000000000000000000006E',
SCROLL_GOV_EXECUTOR: '0x0000000000000000000000000000000000000000',
SCROLL_GOV_EXECUTOR_SEPOLIA: '0x0000000000000000000000000000000000000000',
L1_SCROLL_MESSENGER: '0x6774Bcbd5ceCeF1336b5300fb5186a12DDD8b367',
L2_SCROLL_MESSENGER: '0x781e90f1c8Fc4611c9b7497C3B47F99Ef6969CbC',
L1_SCROLL_MESSENGER_SEPOLIA: '0x50c7d3e7f7c656493D1D76aaa1a836CedfCBB16A',
L2_SCROLL_MESSENGER_SEPOLIA: '0xBa50f5340FB9F3Bd074bD638c9BE13eCB36E603d',
SCROLL_GUARDIAN: '0x0000000000000000000000000000000000000000'
};

export const CONSTANTS = {
Expand Down
46 changes: 46 additions & 0 deletions helpers/scroll-contract-getters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Signer, BigNumber } from 'ethers';
import {
ScrollBridgeExecutor,
ScrollBridgeExecutor__factory,
MockL1ScrollMessenger,
MockL1ScrollMessenger__factory,
MockL2ScrollMessenger,
MockL2ScrollMessenger__factory,
} from '../typechain';
import { tEthereumAddress } from './types';

export const deployScrollMessengers = async (
signer: Signer
): Promise<[MockL1ScrollMessenger, MockL2ScrollMessenger]> => {
const l1Messenger = await new MockL1ScrollMessenger__factory(signer).deploy();
const l2Messenger = await new MockL2ScrollMessenger__factory(signer).deploy();
await l1Messenger.deployTransaction.wait();
await l2Messenger.deployTransaction.wait();
await l1Messenger.setL2Messenger(l2Messenger.address);
await l2Messenger.setL1Messenger(l1Messenger.address);
return [l1Messenger, l2Messenger];
};

export const deployScrollBridgeExecutor = async (
scrollMessenger: tEthereumAddress,
ethereumExecutor: tEthereumAddress,
delay: BigNumber,
gracePeriod: BigNumber,
minimumDelay: BigNumber,
maximumDelay: BigNumber,
guardian: tEthereumAddress,
signer: Signer
): Promise<ScrollBridgeExecutor> => {
const scrollBridgeExecutorFactory = new ScrollBridgeExecutor__factory(signer);
const scrollBridgeExecutor = await scrollBridgeExecutorFactory.deploy(
scrollMessenger,
ethereumExecutor,
delay,
gracePeriod,
minimumDelay,
maximumDelay,
guardian
);
await scrollBridgeExecutor.deployTransaction.wait();
return scrollBridgeExecutor;
};
Loading