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 8, 2023
1 parent bebf6c7 commit 0923ba1
Show file tree
Hide file tree
Showing 4 changed files with 1,498 additions and 0 deletions.
334 changes: 334 additions & 0 deletions contracts/UpdatedForwarder.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
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 UpdatedForwarder is IERC721Receiver, ERC1155Receiver, IForwarder {
// Address to which any funds sent to this contract will be forwarded
address public parentAddress;
address public feeAddress;
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 {
require(_parentAddress != address(0x0), 'Invalid address');
parentAddress = _parentAddress;
require(_feeAddress != address(0x0), 'Invalid address');
feeAddress = _feeAddress;

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 from the allowed addresses
*/
modifier onlyAllowedAddress() {
require(
msg.sender == parentAddress || msg.sender == feeAddress,
'Address is not allowed'
);
_;
}

/**
* 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
onlyAllowedAddress
{
autoFlush721 = autoFlush;
}

/**
* @inheritdoc IForwarder
*/
function setAutoFlush1155(bool autoFlush)
external
virtual
override
onlyAllowedAddress
{
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 callFromAllowedAddress(
address target,
uint256 value,
bytes calldata data
) external onlyAllowedAddress returns (bytes memory) {
(bool success, bytes memory returnedData) = target.call{ value: value }(
data
);
require(success, 'Allowed 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
onlyAllowedAddress
{
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
onlyAllowedAddress
{
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
onlyAllowedAddress
{
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 onlyAllowedAddress {
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
view
virtual
override(ERC1155Receiver, IERC165)
returns (bool)
{
return
interfaceId == type(IForwarder).interfaceId ||
super.supportsInterface(interfaceId);
}
}
54 changes: 54 additions & 0 deletions contracts/UpdatedForwarderFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import './UpdatedForwarder.sol';
import './CloneFactory.sol';

contract UpdatedForwarderFactory is CloneFactory {
address public implementationAddress;

event ForwarderCreated(
address newForwarderAddress,
address parentAddress,
address feeAddress,
bool shouldAutoFlushERC721,
bool shouldAutoFlushERC1155
);

constructor(address _implementationAddress) {
implementationAddress = _implementationAddress;
}

function createForwarder(
address parent,
address feeAddress,
bytes32 salt
) external {
this.createForwarder(parent, feeAddress, salt, true, true);
}

function createForwarder(
address parent,
address feeAddress,
bytes32 salt,
bool shouldAutoFlushERC721,
bool shouldAutoFlushERC1155
) external {
// include the signers in the salt so any contract deployed to a given address must have the same signers
bytes32 finalSalt = keccak256(abi.encodePacked(parent, salt));

address payable clone = createClone(implementationAddress, finalSalt);
UpdatedForwarder(clone).init(
parent,
feeAddress,
shouldAutoFlushERC721,
shouldAutoFlushERC1155
);
emit ForwarderCreated(
clone,
parent,
feeAddress,
shouldAutoFlushERC721,
shouldAutoFlushERC1155
);
}
}
Loading

0 comments on commit 0923ba1

Please sign in to comment.