-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(forwarders): add dandelion voting forwarder
- Loading branch information
Showing
6 changed files
with
1,300 additions
and
20 deletions.
There are no files selected for viewing
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,54 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.17; | ||
|
||
import {Context} from "@openzeppelin/contracts/utils/Context.sol"; | ||
import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; | ||
import {IERC1820Registry} from "@openzeppelin/contracts/interfaces/IERC1820Registry.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {IForwarder} from "../interfaces/IForwarder.sol"; | ||
import {IPReceiver} from "../interfaces/external/IPReceiver.sol"; | ||
import {IPToken} from "../interfaces/external/IPToken.sol"; | ||
import {Helpers} from "../libraries/Helpers.sol"; | ||
import {BytesLib} from "../libraries/BytesLib.sol"; | ||
import "hardhat/console.sol"; | ||
|
||
error CallFailed(address target, bytes data); | ||
error InvalidCallParams(address[] targets, bytes[] data, address caller); | ||
error InvalidOriginAddress(address originAddress); | ||
error InvalidCaller(address caller, address expected); | ||
|
||
contract ForwarderHostPermissioned is IForwarder, Context, Ownable { | ||
using SafeERC20 for IERC20; | ||
|
||
address public immutable caller; | ||
address public immutable token; | ||
mapping(address => bool) private _whitelistedOriginAddresses; | ||
|
||
constructor(address _caller, address _token) { | ||
caller = _caller; | ||
token = _token; | ||
} | ||
|
||
modifier onlyAdmitted() { | ||
address msgSender = _msgSender(); | ||
if (caller != msgSender) { | ||
revert InvalidCaller(msgSender, caller); | ||
} | ||
_; | ||
} | ||
|
||
/// @inheritdoc IForwarder | ||
function call(uint256 amount, address to, bytes calldata data, bytes4 chainId) external onlyAdmitted() { | ||
address msgSender = _msgSender(); | ||
if (amount > 0) { | ||
IERC20(token).safeTransferFrom(msgSender, address(this), amount); | ||
} | ||
|
||
bytes memory effectiveUserData = abi.encode(data, msgSender); | ||
uint256 effectiveAmount = amount == 0 ? 1 : amount; | ||
IPToken(token).redeem(effectiveAmount, effectiveUserData, Helpers.addressToAsciiString(to), chainId); | ||
} | ||
} |
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,105 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.17; | ||
|
||
import {Context} from "@openzeppelin/contracts/utils/Context.sol"; | ||
import {IERC777Recipient} from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; | ||
import {IERC1820Registry} from "@openzeppelin/contracts/interfaces/IERC1820Registry.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {IForwarder} from "../interfaces/IForwarder.sol"; | ||
import {IErc20Vault} from "../interfaces/external/IErc20Vault.sol"; | ||
import {IPToken} from "../interfaces/external/IPToken.sol"; | ||
import {Helpers} from "../libraries/Helpers.sol"; | ||
import {BytesLib} from "../libraries/BytesLib.sol"; | ||
|
||
error CallFailed(address target, bytes data); | ||
error InvalidCallParams(address[] targets, bytes[] data, address caller); | ||
error InvalidOriginAddress(address originAddress); | ||
error InvalidCaller(address caller, address expected); | ||
|
||
contract ForwarderNativePermissioned is IForwarder, IERC777Recipient, Context, Ownable { | ||
using SafeERC20 for IERC20; | ||
|
||
address public immutable token; | ||
address public immutable vault; | ||
mapping(address => bool) private _whitelistedOriginAddresses; | ||
|
||
constructor(address _token, address _vault) { | ||
IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24).setInterfaceImplementer( | ||
address(this), | ||
keccak256("ERC777TokensRecipient"), | ||
address(this) | ||
); | ||
|
||
token = _token; | ||
vault = _vault; // set it to 0 on an host chain | ||
} | ||
|
||
modifier onlySelf() { | ||
address msgSender = _msgSender(); | ||
if (address(this) != msgSender) { | ||
revert InvalidCaller(msgSender, address(this)); | ||
} | ||
_; | ||
} | ||
|
||
function tokensReceived( | ||
address /*_operator*/, | ||
address _from, | ||
address /*_to,*/, | ||
uint256 /*_amount*/, | ||
bytes calldata _userData, | ||
bytes calldata /*_operatorData*/ | ||
) external override { | ||
if (_msgSender() == token && _from == vault) { | ||
(, bytes memory userData, , address originAddress, , , , ) = abi.decode( | ||
_userData, | ||
(bytes1, bytes, bytes4, address, bytes4, address, bytes, bytes) | ||
); | ||
|
||
(bytes memory callsAndTargets, address caller) = abi.decode(userData, (bytes, address)); | ||
|
||
if (!_whitelistedOriginAddresses[originAddress]) { | ||
revert InvalidOriginAddress(originAddress); | ||
} | ||
|
||
(address[] memory targets, bytes[] memory data) = abi.decode(callsAndTargets, (address[], bytes[])); | ||
|
||
if (targets.length != data.length) { | ||
revert InvalidCallParams(targets, data, caller); | ||
} | ||
|
||
for (uint256 i = 0; i < targets.length; ) { | ||
(bool success, ) = targets[i].call(data[i]); | ||
if (!success) { | ||
revert CallFailed(targets[i], data[i]); | ||
} | ||
unchecked { | ||
++i; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// @inheritdoc IForwarder | ||
function call(uint256 amount, address to, bytes calldata data, bytes4 chainId) external onlySelf() { | ||
_call(token, amount, to, data, chainId); | ||
} | ||
|
||
function call(address _token, uint256 amount, address to, bytes calldata data, bytes4 chainId) external onlySelf() { | ||
_call(_token, amount, to, data, chainId); | ||
} | ||
|
||
function _call(address _token, uint256 amount, address to, bytes calldata data, bytes4 chainId) internal { | ||
bytes memory effectiveUserData = abi.encode(data, address(this)); | ||
uint256 effectiveAmount = amount == 0 ? 1 : amount; | ||
IERC20(_token).safeApprove(vault, effectiveAmount); | ||
IErc20Vault(vault).pegIn(effectiveAmount, _token, Helpers.addressToAsciiString(to), effectiveUserData, chainId); | ||
} | ||
|
||
function whitelistOriginAddress(address originAddress) external onlyOwner { | ||
_whitelistedOriginAddresses[originAddress] = true; | ||
} | ||
} |
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
Oops, something went wrong.