Skip to content

Commit

Permalink
feat(contracts): add new forwarder contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
gianchandania committed Oct 4, 2023
1 parent 47976e3 commit a486408
Show file tree
Hide file tree
Showing 10 changed files with 1,800 additions and 4 deletions.
2 changes: 1 addition & 1 deletion contracts/CloneFactory.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
// from https://github.com/optionality/clone-factory
pragma solidity 0.8.10;
pragma solidity 0.8.15;

/*
The MIT License (MIT)
Expand Down
2 changes: 1 addition & 1 deletion contracts/ERC20Interface.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
pragma solidity 0.8.15;

/**
* Contract that exposes the needed erc20 token functions
Expand Down
2 changes: 1 addition & 1 deletion contracts/IForwarder.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.8.0;
pragma solidity 0.8.15;

import '@openzeppelin/contracts/utils/introspection/IERC165.sol';

Expand Down
332 changes: 332 additions & 0 deletions contracts/NewForwarder.sol
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);
}
}
Loading

0 comments on commit a486408

Please sign in to comment.