-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(contracts): add new forwarder contracts
- Loading branch information
1 parent
47976e3
commit a486408
Showing
10 changed files
with
1,800 additions
and
4 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
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
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,332 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity 0.8.15; | ||
import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol'; | ||
import '@openzeppelin/contracts/token/ERC721/IERC721.sol'; | ||
import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; | ||
import '@openzeppelin/contracts/token/ERC1155/utils/ERC1155Receiver.sol'; | ||
import './ERC20Interface.sol'; | ||
import './TransferHelper.sol'; | ||
import './IForwarder.sol'; | ||
|
||
/** | ||
* Contract that will forward any incoming Ether to the parent address of the contract | ||
* | ||
*/ | ||
contract NewForwarder is IERC721Receiver, ERC1155Receiver, IForwarder { | ||
// Address to which any funds sent to this contract will be forwarded | ||
address public parentAddress; | ||
address public feeAddress; | ||
mapping(address => bool) public whiteListedAddresses; | ||
bool public autoFlush721 = true; | ||
bool public autoFlush1155 = true; | ||
|
||
event ForwarderDeposited(address from, uint256 value, bytes data); | ||
|
||
/** | ||
* Initialize the contract, and sets the destination address to that of the parent address | ||
*/ | ||
function init ( | ||
address _parentAddress, | ||
address _feeAddress, | ||
bool _autoFlush721, | ||
bool _autoFlush1155 | ||
) external onlyUninitialized { | ||
parentAddress = _parentAddress; | ||
feeAddress = _feeAddress; | ||
|
||
whiteListedAddresses[parentAddress] = true; | ||
whiteListedAddresses[feeAddress] = true; | ||
uint256 value = address(this).balance; | ||
|
||
// set whether we want to automatically flush erc721/erc1155 tokens or not | ||
autoFlush721 = _autoFlush721; | ||
autoFlush1155 = _autoFlush1155; | ||
|
||
if (value == 0) { | ||
return; | ||
} | ||
|
||
(bool success, ) = parentAddress.call{ value: value }(''); | ||
require(success, 'Flush failed'); | ||
|
||
// NOTE: since we are forwarding on initialization, | ||
// we don't have the context of the original sender. | ||
// We still emit an event about the forwarding but set | ||
// the sender to the forwarder itself | ||
emit ForwarderDeposited(address(this), value, msg.data); | ||
} | ||
|
||
/** | ||
* Modifier that will execute internal code block only if the sender is the whitelisted address | ||
*/ | ||
modifier onlyWhitelistedAddress { | ||
require(whiteListedAddresses[msg.sender], 'Address not in whitelist'); | ||
_; | ||
} | ||
|
||
/** | ||
* Modifier that will execute internal code block only if the contract has not been initialized yet | ||
*/ | ||
modifier onlyUninitialized { | ||
require(parentAddress == address(0x0), 'Already initialized'); | ||
_; | ||
} | ||
|
||
/** | ||
* Default function; Gets called when data is sent but does not match any other function | ||
*/ | ||
fallback() external payable { | ||
flush(); | ||
} | ||
|
||
/** | ||
* Default function; Gets called when Ether is deposited with no data, and forwards it to the parent address | ||
*/ | ||
receive() external payable { | ||
flush(); | ||
} | ||
|
||
/** | ||
* @inheritdoc IForwarder | ||
*/ | ||
function setAutoFlush721(bool autoFlush) | ||
external | ||
virtual | ||
override | ||
onlyWhitelistedAddress | ||
{ | ||
autoFlush721 = autoFlush; | ||
} | ||
|
||
/** | ||
* @inheritdoc IForwarder | ||
*/ | ||
function setAutoFlush1155(bool autoFlush) | ||
external | ||
virtual | ||
override | ||
onlyWhitelistedAddress | ||
{ | ||
autoFlush1155 = autoFlush; | ||
} | ||
|
||
/** | ||
* ERC721 standard callback function for when a ERC721 is transfered. The forwarder will send the nft | ||
* to the base wallet once the nft contract invokes this method after transfering the nft. | ||
* | ||
* @param _operator The address which called `safeTransferFrom` function | ||
* @param _from The address of the sender | ||
* @param _tokenId The token id of the nft | ||
* @param data Additional data with no specified format, sent in call to `_to` | ||
*/ | ||
function onERC721Received( | ||
address _operator, | ||
address _from, | ||
uint256 _tokenId, | ||
bytes memory data | ||
) external virtual override returns (bytes4) { | ||
if (autoFlush721) { | ||
IERC721 instance = IERC721(msg.sender); | ||
require( | ||
instance.supportsInterface(type(IERC721).interfaceId), | ||
'The caller does not support the ERC721 interface' | ||
); | ||
// this won't work for ERC721 re-entrancy | ||
instance.safeTransferFrom(address(this), parentAddress, _tokenId, data); | ||
} | ||
|
||
return this.onERC721Received.selector; | ||
} | ||
|
||
function callFromWhitelistedAddress( | ||
address target, | ||
uint256 value, | ||
bytes calldata data | ||
) external onlyWhitelistedAddress returns (bytes memory) { | ||
(bool success, bytes memory returnedData) = target.call{ value: value }( | ||
data | ||
); | ||
require(success, 'Whitelisted address call execution failed'); | ||
|
||
return returnedData; | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC1155Receiver | ||
*/ | ||
function onERC1155Received( | ||
address _operator, | ||
address _from, | ||
uint256 id, | ||
uint256 value, | ||
bytes calldata data | ||
) external virtual override returns (bytes4) { | ||
IERC1155 instance = IERC1155(msg.sender); | ||
require( | ||
instance.supportsInterface(type(IERC1155).interfaceId), | ||
'The caller does not support the IERC1155 interface' | ||
); | ||
|
||
if (autoFlush1155) { | ||
instance.safeTransferFrom(address(this), parentAddress, id, value, data); | ||
} | ||
|
||
return this.onERC1155Received.selector; | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC1155Receiver | ||
*/ | ||
function onERC1155BatchReceived( | ||
address _operator, | ||
address _from, | ||
uint256[] calldata ids, | ||
uint256[] calldata values, | ||
bytes calldata data | ||
) external virtual override returns (bytes4) { | ||
IERC1155 instance = IERC1155(msg.sender); | ||
require( | ||
instance.supportsInterface(type(IERC1155).interfaceId), | ||
'The caller does not support the IERC1155 interface' | ||
); | ||
|
||
if (autoFlush1155) { | ||
instance.safeBatchTransferFrom( | ||
address(this), | ||
parentAddress, | ||
ids, | ||
values, | ||
data | ||
); | ||
} | ||
|
||
return this.onERC1155BatchReceived.selector; | ||
} | ||
|
||
/** | ||
* @inheritdoc IForwarder | ||
*/ | ||
function flushTokens(address tokenContractAddress) | ||
external | ||
virtual | ||
override | ||
onlyWhitelistedAddress | ||
{ | ||
ERC20Interface instance = ERC20Interface(tokenContractAddress); | ||
address forwarderAddress = address(this); | ||
uint256 forwarderBalance = instance.balanceOf(forwarderAddress); | ||
if (forwarderBalance == 0) { | ||
return; | ||
} | ||
|
||
TransferHelper.safeTransfer( | ||
tokenContractAddress, | ||
parentAddress, | ||
forwarderBalance | ||
); | ||
} | ||
|
||
/** | ||
* @inheritdoc IForwarder | ||
*/ | ||
function flushERC721Token(address tokenContractAddress, uint256 tokenId) | ||
external | ||
virtual | ||
override | ||
onlyWhitelistedAddress | ||
{ | ||
IERC721 instance = IERC721(tokenContractAddress); | ||
require( | ||
instance.supportsInterface(type(IERC721).interfaceId), | ||
'The tokenContractAddress does not support the ERC721 interface' | ||
); | ||
|
||
address ownerAddress = instance.ownerOf(tokenId); | ||
instance.transferFrom(ownerAddress, parentAddress, tokenId); | ||
} | ||
|
||
/** | ||
* @inheritdoc IForwarder | ||
*/ | ||
function flushERC1155Tokens(address tokenContractAddress, uint256 tokenId) | ||
external | ||
virtual | ||
override | ||
onlyWhitelistedAddress | ||
{ | ||
IERC1155 instance = IERC1155(tokenContractAddress); | ||
require( | ||
instance.supportsInterface(type(IERC1155).interfaceId), | ||
'The caller does not support the IERC1155 interface' | ||
); | ||
|
||
address forwarderAddress = address(this); | ||
uint256 forwarderBalance = instance.balanceOf(forwarderAddress, tokenId); | ||
|
||
instance.safeTransferFrom( | ||
forwarderAddress, | ||
parentAddress, | ||
tokenId, | ||
forwarderBalance, | ||
'' | ||
); | ||
} | ||
|
||
/** | ||
* @inheritdoc IForwarder | ||
*/ | ||
function batchFlushERC1155Tokens( | ||
address tokenContractAddress, | ||
uint256[] calldata tokenIds | ||
) external virtual override onlyWhitelistedAddress { | ||
IERC1155 instance = IERC1155(tokenContractAddress); | ||
require( | ||
instance.supportsInterface(type(IERC1155).interfaceId), | ||
'The caller does not support the IERC1155 interface' | ||
); | ||
|
||
address forwarderAddress = address(this); | ||
uint256[] memory amounts = new uint256[](tokenIds.length); | ||
for (uint256 i = 0; i < tokenIds.length; i++) { | ||
amounts[i] = instance.balanceOf(forwarderAddress, tokenIds[i]); | ||
} | ||
|
||
instance.safeBatchTransferFrom( | ||
forwarderAddress, | ||
parentAddress, | ||
tokenIds, | ||
amounts, | ||
'' | ||
); | ||
} | ||
|
||
/** | ||
* Flush the entire balance of the contract to the parent address. | ||
*/ | ||
function flush() public { | ||
uint256 value = address(this).balance; | ||
|
||
if (value == 0) { | ||
return; | ||
} | ||
|
||
(bool success, ) = parentAddress.call{ value: value }(''); | ||
require(success, 'Flush failed'); | ||
emit ForwarderDeposited(msg.sender, value, msg.data); | ||
} | ||
|
||
/** | ||
* @inheritdoc IERC165 | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) | ||
public | ||
virtual | ||
override(ERC1155Receiver, IERC165) | ||
view | ||
returns (bool) | ||
{ | ||
return | ||
interfaceId == type(IForwarder).interfaceId || | ||
super.supportsInterface(interfaceId); | ||
} | ||
} |
Oops, something went wrong.