-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
upgrade opfwdr contracts to v0.8 and implement multiforward (#10933)
* upgrade opfwdr contracts to v0.8 * move dirs, port tests * fix test path * HH test suite pass on v0.8 * add tests for multiforward * prettify * fix linting issues * undo bad contract copy * address review comments * prettier:write * fix pragma * rm empty lines * name mapping k,v
- Loading branch information
1 parent
5b7b401
commit 8134dcf
Showing
14 changed files
with
5,793 additions
and
8 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
contracts/src/v0.8/dev/shared/interfaces/OwnableInterface.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface OwnableInterface { | ||
function owner() external returns (address); | ||
|
||
function transferOwnership(address recipient) external; | ||
|
||
function acceptOwnership() external; | ||
} |
10 changes: 10 additions & 0 deletions
10
contracts/src/v0.8/interfaces/AuthorizedReceiverInterface.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface AuthorizedReceiverInterface { | ||
function isAuthorizedSender(address sender) external view returns (bool); | ||
|
||
function getAuthorizedSenders() external returns (address[] memory); | ||
|
||
function setAuthorizedSenders(address[] calldata senders) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
contracts/src/v0.8/operatorforwarder/dev/AuthorizedForwarder.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.19; | ||
|
||
import {ConfirmedOwnerWithProposal} from "../../shared/access/ConfirmedOwnerWithProposal.sol"; | ||
import {AuthorizedReceiver} from "./AuthorizedReceiver.sol"; | ||
import {Address} from "@openzeppelin/contracts/utils/Address.sol"; | ||
|
||
// solhint-disable custom-errors | ||
contract AuthorizedForwarder is ConfirmedOwnerWithProposal, AuthorizedReceiver { | ||
using Address for address; | ||
|
||
// solhint-disable-next-line chainlink-solidity/prefix-immutable-variables-with-i | ||
address public immutable linkToken; | ||
|
||
event OwnershipTransferRequestedWithMessage(address indexed from, address indexed to, bytes message); | ||
|
||
constructor( | ||
address link, | ||
address owner, | ||
address recipient, | ||
bytes memory message | ||
) ConfirmedOwnerWithProposal(owner, recipient) { | ||
require(link != address(0), "Link token cannot be a zero address"); | ||
linkToken = link; | ||
if (recipient != address(0)) { | ||
emit OwnershipTransferRequestedWithMessage(owner, recipient, message); | ||
} | ||
} | ||
|
||
// solhint-disable-next-line chainlink-solidity/all-caps-constant-storage-variables | ||
string public constant typeAndVersion = "AuthorizedForwarder 1.0.0"; | ||
|
||
// @notice Forward a call to another contract | ||
// @dev Only callable by an authorized sender | ||
// @param to address | ||
// @param data to forward | ||
function forward(address to, bytes calldata data) external validateAuthorizedSender { | ||
require(to != linkToken, "Cannot forward to Link token"); | ||
_forward(to, data); | ||
} | ||
|
||
// @notice Forward multiple calls to other contracts in a multicall style | ||
// @dev Only callable by an authorized sender | ||
// @param tos An array of addresses to forward the calls to | ||
// @param datas An array of data to forward to each corresponding address | ||
function multiForward(address[] calldata tos, bytes[] calldata datas) external validateAuthorizedSender { | ||
require(tos.length == datas.length, "Arrays must have the same length"); | ||
|
||
for (uint256 i = 0; i < tos.length; ++i) { | ||
address to = tos[i]; | ||
require(to != linkToken, "Cannot forward to Link token"); | ||
|
||
// Perform the forward operation | ||
_forward(to, datas[i]); | ||
} | ||
} | ||
|
||
// @notice Forward a call to another contract | ||
// @dev Only callable by the owner | ||
// @param to address | ||
// @param data to forward | ||
function ownerForward(address to, bytes calldata data) external onlyOwner { | ||
_forward(to, data); | ||
} | ||
|
||
// @notice Transfer ownership with instructions for recipient | ||
// @param to address proposed recipient of ownership | ||
// @param message instructions for recipient upon accepting ownership | ||
function transferOwnershipWithMessage(address to, bytes calldata message) external { | ||
transferOwnership(to); | ||
emit OwnershipTransferRequestedWithMessage(msg.sender, to, message); | ||
} | ||
|
||
// @notice concrete implementation of AuthorizedReceiver | ||
// @return bool of whether sender is authorized | ||
function _canSetAuthorizedSenders() internal view override returns (bool) { | ||
return owner() == msg.sender; | ||
} | ||
|
||
// @notice common forwarding functionality and validation | ||
function _forward(address to, bytes calldata data) private { | ||
require(to.isContract(), "Must forward to a contract"); | ||
// solhint-disable-next-line avoid-low-level-calls | ||
(bool success, bytes memory result) = to.call(data); | ||
if (!success) { | ||
if (result.length == 0) revert("Forwarded call reverted without reason"); | ||
assembly { | ||
revert(add(32, result), mload(result)) | ||
} | ||
} | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
contracts/src/v0.8/operatorforwarder/dev/AuthorizedReceiver.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.19; | ||
|
||
import {AuthorizedReceiverInterface} from "../../interfaces/AuthorizedReceiverInterface.sol"; | ||
|
||
// solhint-disable custom-errors | ||
abstract contract AuthorizedReceiver is AuthorizedReceiverInterface { | ||
mapping(address sender => bool authorized) private s_authorizedSenders; | ||
address[] private s_authorizedSenderList; | ||
|
||
event AuthorizedSendersChanged(address[] senders, address changedBy); | ||
|
||
// @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. | ||
// @param senders The addresses of the authorized Chainlink node | ||
function setAuthorizedSenders(address[] calldata senders) external override validateAuthorizedSenderSetter { | ||
require(senders.length > 0, "Must have at least 1 sender"); | ||
// Set previous authorized senders to false | ||
uint256 authorizedSendersLength = s_authorizedSenderList.length; | ||
for (uint256 i = 0; i < authorizedSendersLength; i++) { | ||
s_authorizedSenders[s_authorizedSenderList[i]] = false; | ||
} | ||
// Set new to true | ||
for (uint256 i = 0; i < senders.length; i++) { | ||
require(s_authorizedSenders[senders[i]] == false, "Must not have duplicate senders"); | ||
s_authorizedSenders[senders[i]] = true; | ||
} | ||
// Replace list | ||
s_authorizedSenderList = senders; | ||
emit AuthorizedSendersChanged(senders, msg.sender); | ||
} | ||
|
||
// @notice Retrieve a list of authorized senders | ||
// @return array of addresses | ||
function getAuthorizedSenders() external view override returns (address[] memory) { | ||
return s_authorizedSenderList; | ||
} | ||
|
||
// @notice Use this to check if a node is authorized for fulfilling requests | ||
// @param sender The address of the Chainlink node | ||
// @return The authorization status of the node | ||
function isAuthorizedSender(address sender) public view override returns (bool) { | ||
return s_authorizedSenders[sender]; | ||
} | ||
|
||
// @notice customizable guard of who can update the authorized sender list | ||
// @return bool whether sender can update authorized sender list | ||
function _canSetAuthorizedSenders() internal virtual returns (bool); | ||
|
||
// @notice validates the sender is an authorized sender | ||
function _validateIsAuthorizedSender() internal view { | ||
require(isAuthorizedSender(msg.sender), "Not authorized sender"); | ||
} | ||
|
||
// @notice prevents non-authorized addresses from calling this method | ||
modifier validateAuthorizedSender() { | ||
_validateIsAuthorizedSender(); | ||
_; | ||
} | ||
|
||
// @notice prevents non-authorized addresses from calling this method | ||
modifier validateAuthorizedSenderSetter() { | ||
require(_canSetAuthorizedSenders(), "Cannot set authorized senders"); | ||
_; | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
contracts/src/v0.8/operatorforwarder/dev/LinkTokenReceiver.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.19; | ||
|
||
// solhint-disable custom-errors | ||
abstract contract LinkTokenReceiver { | ||
// @notice Called when LINK is sent to the contract via `transferAndCall` | ||
// @dev The data payload's first 2 words will be overwritten by the `sender` and `amount` | ||
// values to ensure correctness. Calls oracleRequest. | ||
// @param sender Address of the sender | ||
// @param amount Amount of LINK sent (specified in wei) | ||
// @param data Payload of the transaction | ||
function onTokenTransfer( | ||
address sender, | ||
uint256 amount, | ||
bytes memory data | ||
) public validateFromLINK permittedFunctionsForLINK(data) { | ||
assembly { | ||
// solhint-disable-next-line avoid-low-level-calls | ||
mstore(add(data, 36), sender) // ensure correct sender is passed | ||
// solhint-disable-next-line avoid-low-level-calls | ||
mstore(add(data, 68), amount) // ensure correct amount is passed0.8.19 | ||
} | ||
// solhint-disable-next-line avoid-low-level-calls | ||
(bool success, ) = address(this).delegatecall(data); // calls oracleRequest | ||
require(success, "Unable to create request"); | ||
} | ||
|
||
function getChainlinkToken() public view virtual returns (address); | ||
|
||
// @notice Validate the function called on token transfer | ||
function _validateTokenTransferAction(bytes4 funcSelector, bytes memory data) internal virtual; | ||
|
||
// @dev Reverts if not sent from the LINK token | ||
modifier validateFromLINK() { | ||
require(msg.sender == getChainlinkToken(), "Must use LINK token"); | ||
_; | ||
} | ||
|
||
// @dev Reverts if the given data does not begin with the `oracleRequest` function selector | ||
// @param data The data payload of the request | ||
modifier permittedFunctionsForLINK(bytes memory data) { | ||
bytes4 funcSelector; | ||
assembly { | ||
// solhint-disable-next-line avoid-low-level-calls | ||
funcSelector := mload(add(data, 32)) | ||
} | ||
_validateTokenTransferAction(funcSelector, data); | ||
_; | ||
} | ||
} |
Oops, something went wrong.