Skip to content

Commit

Permalink
feat: add forwarders (#72)
Browse files Browse the repository at this point in the history
* feat: add forwarders

* feat: bump address book
  • Loading branch information
sakulstra authored Mar 23, 2023
1 parent 78b55aa commit 49e85e7
Show file tree
Hide file tree
Showing 22 changed files with 647 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/aave-address-book"]
path = lib/aave-address-book
url = https://github.com/bgd-labs/aave-address-book
[submodule "lib/governance-crosschain-bridges"]
path = lib/governance-crosschain-bridges
url = https://github.com/aave/governance-crosschain-bridges
23 changes: 13 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,19 @@ test-rates-factory:; forge test -vvv --match-path src/test/V3RateStrategyFactory


# Scripts
deploy-engine-eth :; forge script script/AaveV3ConfigEngine.s.sol:DeployEngineEth --rpc-url mainnet --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-opt :; forge script script/AaveV3ConfigEngine.s.sol:DeployEngineOpt --rpc-url optimism --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-arb :; forge script script/AaveV3ConfigEngine.s.sol:DeployEngineArb --rpc-url arbitrum --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-pol :; forge script script/AaveV3ConfigEngine.s.sol:DeployEnginePol --rpc-url polygon --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-ava :; forge script script/AaveV3ConfigEngine.s.sol:DeployEngineAva --rpc-url avalanche --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-eth :; forge script script/V3RateStrategyFactory.s.sol:DeployRatesFactoryEth --rpc-url mainnet --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-pol :; forge script script/V3RateStrategyFactory.s.sol:DeployRatesFactoryPol --rpc-url polygon --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-opt :; forge script script/V3RateStrategyFactory.s.sol:DeployRatesFactoryOpt --rpc-url optimism --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-arb :; forge script script/V3RateStrategyFactory.s.sol:DeployRatesFactoryArb --rpc-url arbitrum --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-ava :; forge script script/V3RateStrategyFactory.s.sol:DeployRatesFactoryAva --rpc-url avalanche --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-eth :; forge script scripts/AaveV3ConfigEngine.s.sol:DeployEngineEth --rpc-url mainnet --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-opt :; forge script scripts/AaveV3ConfigEngine.s.sol:DeployEngineOpt --rpc-url optimism --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-arb :; forge script scripts/AaveV3ConfigEngine.s.sol:DeployEngineArb --rpc-url arbitrum --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-pol :; forge script scripts/AaveV3ConfigEngine.s.sol:DeployEnginePol --rpc-url polygon --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-engine-ava :; forge script scripts/AaveV3ConfigEngine.s.sol:DeployEngineAva --rpc-url avalanche --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-eth :; forge script scripts/V3RateStrategyFactory.s.sol:DeployRatesFactoryEth --rpc-url mainnet --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-pol :; forge script scripts/V3RateStrategyFactory.s.sol:DeployRatesFactoryPol --rpc-url polygon --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-opt :; forge script scripts/V3RateStrategyFactory.s.sol:DeployRatesFactoryOpt --rpc-url optimism --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-arb :; forge script scripts/V3RateStrategyFactory.s.sol:DeployRatesFactoryArb --rpc-url arbitrum --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-rates-factory-ava :; forge script scripts/V3RateStrategyFactory.s.sol:DeployRatesFactoryAva --rpc-url avalanche --broadcast --legacy --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-forwarder-pol :; forge script scripts/CrosschainForwarders.s.sol:DeployPol --rpc-url mainnet --broadcast --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-forwarder-opt :; forge script scripts/CrosschainForwarders.s.sol:DeployOpt --rpc-url mainnet --broadcast --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv
deploy-forwarder-arb :; forge script scripts/CrosschainForwarders.s.sol:DeployArb --rpc-url mainnet --broadcast --ledger --mnemonic-indexes ${MNEMONIC_INDEX} --sender ${LEDGER_SENDER} --verify -vvvv

# Utilities
download :; cast etherscan-source --chain ${chain} -d src/etherscan/${chain}_${address} ${address}
Expand Down
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 2 files
+2 −2 package.json
+11 −1 src/Vm.sol
1 change: 1 addition & 0 deletions lib/governance-crosschain-bridges
2 changes: 1 addition & 1 deletion lib/solidity-utils
3 changes: 2 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ aave-v3-core/=lib/aave-address-book/lib/aave-v3-core/
aave-v3-periphery/=lib/aave-address-book/lib/aave-v3-periphery/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
solidity-utils/=lib/solidity-utils/src/
solidity-utils/=lib/solidity-utils/src/
governance-crosschain-bridges/=lib/governance-crosschain-bridges/
File renamed without changes.
25 changes: 25 additions & 0 deletions scripts/CrosschainForwarders.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import './Utils.s.sol';
import {CrosschainForwarderPolygon} from '../src/crosschainforwarders/CrosschainForwarderPolygon.sol';
import {CrosschainForwarderOptimism} from '../src/crosschainforwarders/CrosschainForwarderOptimism.sol';
import {CrosschainForwarderArbitrum} from '../src/crosschainforwarders/CrosschainForwarderArbitrum.sol';

contract DeployPol is EthereumScript {
function run() external broadcast {
new CrosschainForwarderPolygon();
}
}

contract DeployOpt is EthereumScript {
function run() external broadcast {
new CrosschainForwarderOptimism();
}
}

contract DeployArb is EthereumScript {
function run() external broadcast {
new CrosschainForwarderArbitrum();
}
}
File renamed without changes.
File renamed without changes.
110 changes: 110 additions & 0 deletions src/crosschainforwarders/CrosschainForwarderArbitrum.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {AaveGovernanceV2} from 'aave-address-book/AaveGovernanceV2.sol';
import {AaveV3Arbitrum} from 'aave-address-book/AaveV3Arbitrum.sol';
import {IInbox} from 'governance-crosschain-bridges/contracts/dependencies/arbitrum/interfaces/IInbox.sol';
import {IL2BridgeExecutor} from 'governance-crosschain-bridges/contracts/interfaces/IL2BridgeExecutor.sol';

/**
* @title A generic executor for proposals targeting the arbitrum v3 pool
* @author BGD Labs
* @notice You can **only** use this executor when the arbitrum payload has a `execute()` signature without parameters
* @notice You can **only** use this executor when the arbitrum payload is expected to be executed via `DELEGATECALL`
* @notice This contract assumes to be called via AAVE Governance V2
* @notice This contract will assume the SHORT_EXECUTOR will be topped up with enough funds to fund the short executor
* @dev This executor is a generic wrapper to be used with Arbitrum Inbox (https://developer.offchainlabs.com/arbos/l1-to-l2-messaging)
* It encodes a parameterless `execute()` with delegate calls and a specified target.
* This encoded abi is then send to the Inbox to be synced executed on the arbitrum network.
* Once synced the ARBITRUM_BRIDGE_EXECUTOR will queue the execution of the payload.
*/
contract CrosschainForwarderArbitrum {
IInbox public constant INBOX = IInbox(0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f);
address public constant ARBITRUM_BRIDGE_EXECUTOR = AaveGovernanceV2.ARBITRUM_BRIDGE_EXECUTOR;
address public constant ARBITRUM_GUARDIAN = 0xbbd9f90699c1FA0D7A65870D241DD1f1217c96Eb;

// amount of gwei to overpay on basefee for fast submission
uint256 public constant BASE_FEE_MARGIN = 10 gwei;

/**
* @dev calculateRetryableSubmissionFee on `0x00000000000000000000000000000000000000C8` for a queue call with 1 slot will yield a constant gasLimit of `429478`
* To account for some margin we rounded up to 450000
*/
uint256 public constant L2_GAS_LIMIT = 450000;

/**
* @dev There is currently no oracle on L1 exposing gasPrice of arbitrum. Therefore we overpay by assuming 1 gwei (10x of current price).
*/
uint256 public constant L2_MAX_FEE_PER_GAS = 1 gwei;

/**
* @dev returns the amount of gas needed for submitting the ticket
* @param bytesLength the payload bytes length (usually 580)
* @return uint256 maxSubmissionFee needed on L2 with BASE_FEE_MARGIN
* @return uint256 estimated L2 redepmption fee
*/
function getRequiredGas(uint256 bytesLength) public view returns (uint256, uint256) {
return (
INBOX.calculateRetryableSubmissionFee(bytesLength, block.basefee + BASE_FEE_MARGIN),
L2_GAS_LIMIT * L2_MAX_FEE_PER_GAS
);
}

/**
* @dev checks if the short executor is topped up with enough eth for proposal execution
* with current basefee
* @param bytesLength the payload bytes length (usually 580)
* @return bool indicating if the SHORT_EXECUTOR has sufficient funds
* @return uint256 the gas required for ticket creation and redemption
*/
function hasSufficientGasForExecution(uint256 bytesLength) public view returns (bool, uint256) {
(uint256 maxSubmission, uint256 maxRedemption) = getRequiredGas(bytesLength);
uint256 requiredGas = maxSubmission + maxRedemption;
return (AaveGovernanceV2.SHORT_EXECUTOR.balance >= requiredGas, requiredGas);
}

/**
* @dev encodes the queue call which is forwarded to arbitrum
* @param l2PayloadContract the address of the arbitrum payload
*/
function getEncodedPayload(address l2PayloadContract) public pure returns (bytes memory) {
address[] memory targets = new address[](1);
targets[0] = l2PayloadContract;
uint256[] memory values = new uint256[](1);
values[0] = 0;
string[] memory signatures = new string[](1);
signatures[0] = 'execute()';
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = '';
bool[] memory withDelegatecalls = new bool[](1);
withDelegatecalls[0] = true;
return
abi.encodeWithSelector(
IL2BridgeExecutor.queue.selector,
targets,
values,
signatures,
calldatas,
withDelegatecalls
);
}

/**
* @dev this function will be executed once the proposal passes the mainnet vote.
* @param l2PayloadContract the arbitrum contract containing the `execute()` signature.
*/
function execute(address l2PayloadContract) public {
bytes memory queue = getEncodedPayload(l2PayloadContract);
(uint256 maxSubmission, uint256 maxRedemption) = getRequiredGas(queue.length);
INBOX.unsafeCreateRetryableTicket{value: maxSubmission + maxRedemption}(
ARBITRUM_BRIDGE_EXECUTOR,
0, // l2CallValue
maxSubmission, // maxSubmissionCost
address(ARBITRUM_BRIDGE_EXECUTOR), // excessFeeRefundAddress
address(ARBITRUM_GUARDIAN), // callValueRefundAddress
L2_GAS_LIMIT, // gasLimit
L2_MAX_FEE_PER_GAS, // maxFeePerGas
queue
);
}
}
74 changes: 74 additions & 0 deletions src/crosschainforwarders/CrosschainForwarderOptimism.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {ICrossDomainMessenger} from 'governance-crosschain-bridges/contracts/dependencies/optimism/interfaces/ICrossDomainMessenger.sol';
import {IL2BridgeExecutor} from 'governance-crosschain-bridges/contracts/interfaces/IL2BridgeExecutor.sol';

interface ICanonicalTransactionChain {
function enqueueL2GasPrepaid() external view returns (uint256);
}

/**
* @title A generic executor for proposals targeting the optimism v3 pool
* @author BGD Labs
* @notice You can **only** use this executor when the optimism payload has a `execute()` signature without parameters
* @notice You can **only** use this executor when the optimism payload is expected to be executed via `DELEGATECALL`
* @notice You can **only** execute payloads on optimism with up to prepayed gas which is specified in `enqueueL2GasPrepaid` gas.
* Prepaid gas is the maximum gas covered by the bridge without additional payment.
* @dev This executor is a generic wrapper to be used with Optimism CrossDomainMessenger (https://etherscan.io/address/0x25ace71c97b33cc4729cf772ae268934f7ab5fa1)
* It encodes and sends via the L2CrossDomainMessenger a message to queue for execution an action on L2, in the Aave OPTIMISM_BRIDGE_EXECUTOR.
*/
contract CrosschainForwarderOptimism {
/**
* @dev The L1 Cross Domain Messenger contract sends messages from L1 to L2, and relays messages
* from L2 onto L1. In this contract it's used by the governance SHORT_EXECUTOR to send the encoded L2 queuing over the bridge.
*/
address public constant L1_CROSS_DOMAIN_MESSENGER_ADDRESS =
0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1;

/**
* @dev The optimism bridge executor is a L2 governance execution contract.
* This contract allows queuing of proposals by allow listed addresses (in this case the L1 short executor).
* https://optimistic.etherscan.io/address/0x7d9103572bE58FfE99dc390E8246f02dcAe6f611
*/
address public constant OPTIMISM_BRIDGE_EXECUTOR = 0x7d9103572bE58FfE99dc390E8246f02dcAe6f611;

/**
* @dev The CTC contract is an append only log of transactions which must be applied to the rollup state.
* It also holds configurations like the currently prepayed amount of gas which is what this contract is utilizing.
* https://etherscan.io/address/0x5e4e65926ba27467555eb562121fac00d24e9dd2#code
*/
ICanonicalTransactionChain public constant CANONICAL_TRANSACTION_CHAIN =
ICanonicalTransactionChain(0x5E4e65926BA27467555EB562121fac00D24E9dD2);

/**
* @dev this function will be executed once the proposal passes the mainnet vote.
* @param l2PayloadContract the optimism contract containing the `execute()` signature.
*/
function execute(address l2PayloadContract) public {
address[] memory targets = new address[](1);
targets[0] = l2PayloadContract;
uint256[] memory values = new uint256[](1);
values[0] = 0;
string[] memory signatures = new string[](1);
signatures[0] = 'execute()';
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = '';
bool[] memory withDelegatecalls = new bool[](1);
withDelegatecalls[0] = true;

bytes memory queue = abi.encodeWithSelector(
IL2BridgeExecutor.queue.selector,
targets,
values,
signatures,
calldatas,
withDelegatecalls
);
ICrossDomainMessenger(L1_CROSS_DOMAIN_MESSENGER_ADDRESS).sendMessage(
OPTIMISM_BRIDGE_EXECUTOR,
queue,
uint32(CANONICAL_TRANSACTION_CHAIN.enqueueL2GasPrepaid())
);
}
}
39 changes: 39 additions & 0 deletions src/crosschainforwarders/CrosschainForwarderPolygon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IFxStateSender} from 'governance-crosschain-bridges/contracts/dependencies/polygon/fxportal/FxRoot.sol';

/**
* @title A generic executor for proposals targeting the polygon v3 pool
* @author BGD Labs
* @notice You can **only** use this executor when the polygon payload has a `execute()` signature without parameters
* @notice You can **only** use this executor when the polygon payload is expected to be executed via `DELEGATECALL`
* @dev This executor is a generic wrapper to be used with FX bridges (https://github.com/fx-portal/contracts)
* It encodes a parameterless `execute()` with delegate calls and a specified target.
* This encoded abi is then send to the FX-root to be synced to the FX-child on the polygon network.
* Once synced the POLYGON_BRIDGE_EXECUTOR will queue the execution of the payload.
*/
contract CrosschainForwarderPolygon {
address public constant FX_ROOT_ADDRESS = 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2;
address public constant POLYGON_BRIDGE_EXECUTOR = 0xdc9A35B16DB4e126cFeDC41322b3a36454B1F772;

/**
* @dev this function will be executed once the proposal passes the mainnet vote.
* @param l2PayloadContract the polygon contract containing the `execute()` signature.
*/
function execute(address l2PayloadContract) public {
address[] memory targets = new address[](1);
targets[0] = l2PayloadContract;
uint256[] memory values = new uint256[](1);
values[0] = 0;
string[] memory signatures = new string[](1);
signatures[0] = 'execute()';
bytes[] memory calldatas = new bytes[](1);
calldatas[0] = '';
bool[] memory withDelegatecalls = new bool[](1);
withDelegatecalls[0] = true;

bytes memory actions = abi.encode(targets, values, signatures, calldatas, withDelegatecalls);
IFxStateSender(FX_ROOT_ADDRESS).sendMessageToChild(POLYGON_BRIDGE_EXECUTOR, actions);
}
}
Loading

0 comments on commit 49e85e7

Please sign in to comment.