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 Dec 27, 2023
1 parent 03fd0cf commit 31edb46
Show file tree
Hide file tree
Showing 8 changed files with 1,748 additions and 3 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Multi-sig contract suitable for use as a 2-of-3 multisig wallet.
The core functionality of the wallet is implemented in the [WalletSimple](contracts/WalletSimple.sol) contract. It is initialized with 3 signer addresses, two of which must participate in order to execute a transaction from the wallet.
Auxillary contracts called [Forwarders](contracts/Forwarder.sol) can be deployed with a WalletSimple contract initialized as its "parent". Any funds that the forwarder receives will be sent on back to the parent wallet. This enables omnibus-style wallets to create many addresses that are all controlled by the same wallet.

New forwarder contracts (contracts/ForwarderV4.sol) can be deployed and initialized with a parent address and fee address. Parent address will be the single sig base address of the wallet. Fee address will be the gas tank address of the wallet. Both parent address and fee address will be allowed to invoke methods of the contract, but any funds that the forwarder receives will be sent on back to the parent address. This enables omnibus-style wallets to create many addresses that are all controlled by the same wallet.

Features of the [wallet contract](contracts/WalletSimple.sol):

1. Functions as a 2-of-3 multisig wallet for sending transactions.
Expand All @@ -22,6 +24,13 @@ Features of the [forwarder contract](contracts/Forwarder.sol)
2. Automatically flushes any ETH received to the parent address.
3. Able to flush ERC20 tokens received to the parent address through a separate transaction (flushForwarderTokens).

Features of the updated [forwarder contract](contracts/ForwarderV4.sol)

1. Deployed with a permanent parent address and a fee address.
2. Both parent address and fee address can invoke the methods of the contract.
3. Automatically flushes any ETH received to the parent address.
4. Able to flush ERC20 tokens received to the parent address through a separate transaction (flushTokens).

Note that this suite of contracts is an upgraded version of [eth-multisig-v2](https://github.com/bitgo/eth-multisig-v2). The main changes that were made are as follows:
- Wallets and forwarders are deployed as proxy instances to a single implementation, to save on deployment fees.
- Wallets and forwarders are deployed using CREATE2 to allow addresses to be generated on demand, but only deployed upon first use.
Expand All @@ -48,6 +57,13 @@ To deploy forwarders, follow these steps:
3. Call the `createForwarder` function on the factory deployed in step 2. Provide the parent address, and some "salt" which will be used to determine the forwarder's address via [CREATE2](https://eips.ethereum.org/EIPS/eip-1014).
4. Check for the `ForwarderCreated` event from the above transaction. This will include your newly generated forwarder address

**ForwardersV4**
To deploy forwarders, follow these steps:
1. Deploy a forwarder contract ([contracts/ForwarderV4.sol](contracts/Forwarder.sol)) with any address. Take note of the contract's address.
2. Deploy a ForwarderFactory contract ([contracts/ForwarderFactoryV4.sol](contracts/ForwarderFactoryV4.sol)) with any address. Use the address of the contract deployed in step 1 as the `_implementationAddress` parameter.
3. Call the `createForwarder` function on the factory deployed in step 2. Provide the parent address, fee address and some "salt" which will be used to determine the forwarder's address via [CREATE2](https://eips.ethereum.org/EIPS/eip-1014).
4. Check for the `ForwarderCreated` event from the above transaction. This will include your newly generated forwarder address


## Contracts
Brief descriptions of the various contracts provided in this repository.
Expand All @@ -72,6 +88,14 @@ Factory to create forwarder. Deploys a small proxy which utilizes the implementa

Transfer batcher. Takes a list of recipients and amounts, and distributes ETH to them in a single transaction.

[**ForwarderV4**](contracts/ForwarderV4.sol)

Forwarder function. Initializes with a parent and a fee address. It will forward any ETH that it receives to the parent address. Also has a function to forward ERC20 tokens. This function can be invoked using the parent address or the fee address.

[**ForwarderFactoryV4**](contracts/ForwarderFactoryV4.sol)

Factory to create updated forwarder (contracts/ForwarderV4.sol). Deploys a small proxy which utilizes the implementation of a single forwarder contract.

## Installation

NodeJS 16 is required.
Expand Down
84 changes: 84 additions & 0 deletions contracts/ForwarderFactoryV4.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import './ForwarderV4.sol';
import './CloneFactory.sol';

/**
* @title ForwarderFactoryV4
* @notice This contract will deploy new forwarder contracts using the create2 opcode
*/
contract ForwarderFactoryV4 is CloneFactory {
address public implementationAddress;

/**
* @notice Event triggered when a new forwarder is deployed
* @param newForwarderAddress Address of the newly deployed forwarder
* @param parentAddress Address to which the funds should be forwarded
* @param feeAddress Address which is allowed to call methods on forwarder contract alongwith the parentAddress
* @param shouldAutoFlushERC721 Whether to automatically flush ERC721 tokens or not
* @param shouldAutoFlushERC1155 Whether to automatically flush ERC1155 tokens or not
*/
event ForwarderCreated(
address newForwarderAddress,
address parentAddress,
address feeAddress,
bool shouldAutoFlushERC721,
bool shouldAutoFlushERC1155
);

/**
* @notice Initializes the factory with the address of the current forwarder implementation
* @param _implementationAddress Address of the current forwarder implementation
*/
constructor(address _implementationAddress) {
implementationAddress = _implementationAddress;
}

/**
* @notice Creates a new forwarder contract
* @param parent Address to which the funds should be forwarded
* @param feeAddress Address which is allowed to call methods on forwarder contract alongwith the parentAddress
* @param salt Salt to be used while deploying the contract
*/
function createForwarder(
address parent,
address feeAddress,
bytes32 salt
) external {
this.createForwarder(parent, feeAddress, salt, true, true);
}

/**
* @notice Creates a new forwarder contract
* @param parent Address to which the funds should be forwarded
* @param feeAddress Address which is allowed to call methods on forwarder contract alongwith the parentAddress
* @param salt Salt to be used while deploying the contract
* @param shouldAutoFlushERC721 Whether to automatically flush ERC721 tokens or not
* @param shouldAutoFlushERC1155 Whether to automatically flush ERC1155 tokens or not
*/
function createForwarder(
address parent,
address feeAddress,
bytes32 salt,
bool shouldAutoFlushERC721,
bool shouldAutoFlushERC1155
) external {
/// include the parent and fee address in the salt so any contract deployed directly relies on the parent address and the fee address
bytes32 finalSalt = keccak256(abi.encodePacked(parent, feeAddress, salt));

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

0 comments on commit 31edb46

Please sign in to comment.