Skip to content

Commit

Permalink
Merge pull request #61 from pooltogether/pool-2496-optimism-add-contr…
Browse files Browse the repository at this point in the history
…act-to-bridge-draw

Add contracts to bridge draw
  • Loading branch information
PierrickGT authored Jan 26, 2023
2 parents 23e3e13 + 1e54a9f commit 7221ddd
Show file tree
Hide file tree
Showing 9 changed files with 1,187 additions and 2 deletions.
161 changes: 161 additions & 0 deletions contracts/DrawDispatcher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import { IDrawBeacon } from "@pooltogether/v4-core/contracts/interfaces/IDrawBeacon.sol";
import { IDrawBuffer } from "@pooltogether/v4-core/contracts/interfaces/IDrawBuffer.sol";

import { ISingleMessageDispatcher } from "./interfaces/ISingleMessageDispatcher.sol";

/**
* @title PoolTogether V4 DrawDispatcher
* @author PoolTogether Inc Team
* @notice The DrawDispatcher smart contract relies on ERC-5164 to dispatch draws from Ethereum to another L1 or L2
* where Chainlink VRF 2.0 may not be available to compute draws.
*/
contract DrawDispatcher {
/**
* @notice Emitted when the `draw` has been dispatched.
* @param dispatcher Address of the dispatcher on Ethereum that dispatched the draw
* @param toChainId ID of the receiving chain
* @param drawExecutor Address of the DrawExecutor on the receiving chain that will push the draw onto the DrawBuffer
* @param draw Draw that was dispatched
*/
event DrawDispatched(
ISingleMessageDispatcher indexed dispatcher,
uint256 indexed toChainId,
address indexed drawExecutor,
IDrawBeacon.Draw draw
);

/**
* @notice Emitted when the `draws` have been dispatched.
* @param dispatcher Address of the dispatcher on Ethereum that dispatched the draws
* @param toChainId ID of the receiving chain
* @param drawExecutor Address of the DrawExecutor on the receiving chain that will push the draws onto the DrawBuffer
* @param draws Draws that were dispatched
*/
event DrawsDispatched(
ISingleMessageDispatcher indexed dispatcher,
uint256 indexed toChainId,
address indexed drawExecutor,
IDrawBeacon.Draw[] draws
);

/// @notice DrawBuffer from which draws are retrieved.
IDrawBuffer public immutable drawBuffer;

/**
* @notice DrawDispatcher constructor.
* @param _drawBuffer Address of the DrawBuffer from which draws are retrieved
*/
constructor(IDrawBuffer _drawBuffer) {
require(address(_drawBuffer) != address(0), "DD/drawBuffer-not-zero-address");

drawBuffer = _drawBuffer;
}

/**
* @notice Retrieves and dispatch the newest recorded draw.
* @param _dispatcher Address of the dispatcher on Ethereum that will be used to dispatch the draw
* @param _toChainId ID of the receiving chain
* @param _drawExecutor Address of the DrawExecutor on the receiving chain that will push the draw onto the DrawBuffer
*/
function dispatchNewestDraw(
ISingleMessageDispatcher _dispatcher,
uint256 _toChainId,
address _drawExecutor
) external {
IDrawBeacon.Draw memory _newestDraw = drawBuffer.getNewestDraw();
_dispatchDraw(_dispatcher, _toChainId, _drawExecutor, _newestDraw);
}

/**
* @notice Retrieves and dispatch draw.
* @dev Will revert if the draw does not exist.
* @param _dispatcher Address of the dispatcher on Ethereum that will be used to dispatch the draw
* @param _toChainId ID of the receiving chain
* @param _drawExecutor Address of the DrawExecutor on the receiving chain that will push the draw onto the DrawBuffer
* @param _drawId Id of the draw to dispatch
*/
function dispatchDraw(
ISingleMessageDispatcher _dispatcher,
uint256 _toChainId,
address _drawExecutor,
uint32 _drawId
) external {
require(_drawId > 0, "DD/drawId-gt-zero");

IDrawBeacon.Draw memory _draw = drawBuffer.getDraw(_drawId);
_dispatchDraw(_dispatcher, _toChainId, _drawExecutor, _draw);
}

/**
* @notice Retrieves and dispatch draws.
* @dev `_drawIds` must be ordered in ascending and contiguous order.
* @dev Will revert if one of the draw does not exist.
* @param _dispatcher Address of the dispatcher on Ethereum that will be used to dispatch the draw
* @param _toChainId ID of the receiving chain
* @param _drawExecutor Address of the DrawExecutor on the receiving chain that will push the draw onto the DrawBuffer
* @param _drawIds Array of draw ids to dispatch
*/
function dispatchDraws(
ISingleMessageDispatcher _dispatcher,
uint256 _toChainId,
address _drawExecutor,
uint32[] calldata _drawIds
) external {
IDrawBeacon.Draw[] memory _draws = drawBuffer.getDraws(_drawIds);

_dispatchMessage(
_dispatcher,
_toChainId,
_drawExecutor,
abi.encodeWithSignature("pushDraws((uint256,uint32,uint64,uint64,uint32)[])", _draws)
);

emit DrawsDispatched(_dispatcher, _toChainId, _drawExecutor, _draws);
}

/**
* @notice Dispatch the passed `draw`.
* @param _dispatcher Address of the dispatcher on Ethereum that will be used to dispatch the draw
* @param _toChainId ID of the receiving chain
* @param _drawExecutor Address of the DrawExecutor on the receiving chain that will push the draw onto the DrawBuffer
* @param _draw Draw to dispatch
*/
function _dispatchDraw(
ISingleMessageDispatcher _dispatcher,
uint256 _toChainId,
address _drawExecutor,
IDrawBeacon.Draw memory _draw
) internal {
_dispatchMessage(
_dispatcher,
_toChainId,
_drawExecutor,
abi.encodeWithSignature("pushDraw((uint256,uint32,uint64,uint64,uint32))", _draw)
);

emit DrawDispatched(_dispatcher, _toChainId, _drawExecutor, _draw);
}

/**
* @notice Dispatch encoded call.
* @param _dispatcher Address of the dispatcher on Ethereum that will dispatch the call
* @param _toChainId ID of the receiving chain
* @param _drawExecutor Address of the DrawExecutor on the receiving chain that will receive the call
* @param _data Calldata to dispatch
*/
function _dispatchMessage(
ISingleMessageDispatcher _dispatcher,
uint256 _toChainId,
address _drawExecutor,
bytes memory _data
) internal {
require(address(_dispatcher) != address(0), "DD/dispatcher-not-zero-address");
require(_drawExecutor != address(0), "DD/drawExecutor-not-zero-address");

_dispatcher.dispatchMessage(_toChainId, _drawExecutor, _data);
}
}
102 changes: 102 additions & 0 deletions contracts/DrawExecutor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

import "@pooltogether/v4-core/contracts/interfaces/IDrawBuffer.sol";

import { ExecutorAware } from "./abstract/ExecutorAware.sol";

/**
* @title PoolTogether V4 DrawExecutor
* @author PoolTogether Inc Team
* @notice The DrawExecutor smart contract relies on ERC-5164 to receive draws from Ethereum
* and push them onto the DrawBuffer.
* @dev This contract does not ensure draw ordering and draws should always be bridged in ascending and contiguous order.
*/
contract DrawExecutor is ExecutorAware {
/**
* @notice Emitted when the `draw` has been pushed.
* @param draw Draw that was pushed
*/
event DrawPushed(IDrawBeacon.Draw draw);

/**
* @notice Emitted when the `draws` have been pushed.
* @param draws Draws that were pushed
*/
event DrawsPushed(IDrawBeacon.Draw[] draws);

/// @notice ID of the origin chain.
uint256 public immutable originChainId;

/// @notice DrawDispatcher contract on the origin chain that dispatch the draws.
address public immutable drawDispatcher;

/// @notice DrawBuffer onto which draws are pushed.
IDrawBuffer public immutable drawBuffer;

/**
* @notice DrawExecutor constructor.
* @param _originChainId ID of the origin chain
* @param _drawDispatcher Address of the DrawDispatcher on the origin chain that dispatch the draws
* @param _executor Address of the ERC-5164 contract that executes the bridged calls
* @param _drawBuffer Address of the DrawBuffer onto which draws are pushed
*/
constructor(
uint256 _originChainId,
address _drawDispatcher,
address _executor,
IDrawBuffer _drawBuffer
) ExecutorAware(_executor) {
require(_originChainId != 0, "DE/originChainId-not-zero");
require(address(_drawDispatcher) != address(0), "DE/drawDispatcher-not-zero-adrs");
require(address(_drawBuffer) != address(0), "DE/drawBuffer-not-zero-address");

originChainId = _originChainId;
drawDispatcher = _drawDispatcher;
drawBuffer = _drawBuffer;
}

/**
* @notice Push `draw` onto the DrawBuffer.
* @dev Only the `executor` is able to call this function.
* @param _draw Draw to push
*/
function pushDraw(IDrawBeacon.Draw calldata _draw) external {
_checkSender();

drawBuffer.pushDraw(_draw);

emit DrawPushed(_draw);
}

/**
* @notice Push `draws` onto the DrawBuffer.
* @dev Only the `executor` is able to call this function.
* @dev `draws` must be ordered in ascending and contiguous order.
* @param _draws Draws to push
*/
function pushDraws(IDrawBeacon.Draw[] calldata _draws) external {
_checkSender();

uint256 _drawsLength = _draws.length;

for (uint256 i; i < _drawsLength; i++) {
drawBuffer.pushDraw(_draws[i]);
}

emit DrawsPushed(_draws);
}

/**
* @notice Checks that:
* - the call has been dispatched from the supported chain
* - the sender on the receiving chain is the executor
* - the sender on the origin chain is the DrawDispatcher
*/
function _checkSender() internal view {
require(_fromChainId() == originChainId, "DE/l1-chainId-not-supported");
require(isTrustedExecutor(msg.sender), "DE/l2-sender-not-executor");
require(_msgSender() == address(drawDispatcher), "DE/l1-sender-not-dispatcher");
}
}
84 changes: 84 additions & 0 deletions contracts/abstract/ExecutorAware.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.6;

/**
* @title ExecutorAware abstract contract
* @notice The ExecutorAware contract allows contracts on a receiving chain to execute messages from an origin chain.
* These messages are sent by the `MessageDispatcher` contract which live on the origin chain.
* The `MessageExecutor` contract on the receiving chain executes these messages
* and then forward them to an ExecutorAware contract on the receiving chain.
* @dev This contract implements EIP-2771 (https://eips.ethereum.org/EIPS/eip-2771)
* to ensure that messages are sent by a trusted `MessageExecutor` contract.
*/
abstract contract ExecutorAware {
/* ============ Variables ============ */

/// @notice Address of the trusted executor contract.
address public immutable trustedExecutor;

/* ============ Constructor ============ */

/**
* @notice ExecutorAware constructor.
* @param _executor Address of the `MessageExecutor` contract
*/
constructor(address _executor) {
require(_executor != address(0), "executor-not-zero-address");
trustedExecutor = _executor;
}

/* ============ External Functions ============ */

/**
* @notice Check which executor this contract trust.
* @param _executor Address to check
*/
function isTrustedExecutor(address _executor) public view returns (bool) {
return _executor == trustedExecutor;
}

/* ============ Internal Functions ============ */

/**
* @notice Retrieve messageId from message data.
* @return _msgDataMessageId ID uniquely identifying the message that was executed
*/
function _messageId() internal pure returns (bytes32 _msgDataMessageId) {
_msgDataMessageId;

if (msg.data.length >= 84) {
assembly {
_msgDataMessageId := calldataload(sub(calldatasize(), 84))
}
}
}

/**
* @notice Retrieve fromChainId from message data.
* @return _msgDataFromChainId ID of the chain that dispatched the messages
*/
function _fromChainId() internal pure returns (uint256 _msgDataFromChainId) {
_msgDataFromChainId;

if (msg.data.length >= 52) {
assembly {
_msgDataFromChainId := calldataload(sub(calldatasize(), 52))
}
}
}

/**
* @notice Retrieve signer address from message data.
* @return _signer Address of the signer
*/
function _msgSender() internal view returns (address payable _signer) {
_signer = payable(msg.sender);

if (msg.data.length >= 20 && isTrustedExecutor(_signer)) {
assembly {
_signer := shr(96, calldataload(sub(calldatasize(), 20)))
}
}
}
}
Loading

0 comments on commit 7221ddd

Please sign in to comment.