BitGo deposit forwarder that sweeps ether and ERC-20 tokens to its parent.
Historical Significance
BitGo's forwarder was one of the most widely copied deposit-address designs of the period, and the flush() function exists specifically to recover ether sent to a deposit address before its contract was deployed, a real operational hazard of counterfactual deployment.
Key Facts
Description
BitGo's Forwarder from the eth-multisig-v2 project. Its payable fallback forwards incoming ether straight to parentAddress and emits ForwarderDeposited; flush() pushes any stranded balance to the parent; and flushTokens(address), restricted to the parent, moves an ERC-20 balance across and emits TokensFlushed. Matched against the July 2017 revision of the source. The current version on the project's default branch does not reproduce it, because a later commit removed the TokensFlushed event that this bytecode still emits. The runtime code matches the deployed bytecode byte for byte; only the trailing swarm metadata hash differs, because the EthereumHistory attribution comment changes the source text. Sourcify records this as a partial match and Etherscan shows it as verified.
Source Verified
Byzantium Era
First Metropolis hard fork. Added zk-SNARK precompiles, REVERT opcode, and staticcall.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
// Submitted by EthereumHistory (ethereumhistory.com)
pragma solidity ^0.4.11;
/**
* Contract that exposes the needed erc20 token functions
*/
contract ERC20Interface {
// Send _value amount of tokens to address _to
function transfer(address _to, uint256 _value) returns (bool success);
// Get the account balance of another account with address _owner
function balanceOf(address _owner) constant returns (uint256 balance);
}
/**
* Contract that will forward any incoming Ether to its creator
*/
contract Forwarder {
// Address to which any funds sent to this contract will be forwarded
address public parentAddress;
event ForwarderDeposited(address from, uint value, bytes data);
event TokensFlushed(
address tokenContractAddress, // The contract address of the token
uint value // Amount of token sent
);
/**
* Create the contract, and set the destination address to that of the creator
*/
function Forwarder() {
parentAddress = msg.sender;
}
/**
* Modifier that will execute internal code block only if the sender is a parent of the forwarder contract
*/
modifier onlyParent {
if (msg.sender != parentAddress) {
throw;
}
_;
}
/**
* Default function; Gets called when Ether is deposited, and forwards it to the destination address
*/
function() payable {
if (!parentAddress.call.value(msg.value)(msg.data))
throw;
// Fire off the deposited event if we can forward it
ForwarderDeposited(msg.sender, msg.value, msg.data);
}
/**
* Execute a token transfer of the full balance from the forwarder token to the main wallet contract
* @param tokenContractAddress the address of the erc20 token contract
*/
function flushTokens(address tokenContractAddress) onlyParent {
ERC20Interface instance = ERC20Interface(tokenContractAddress);
var forwarderAddress = address(this);
var forwarderBalance = instance.balanceOf(forwarderAddress);
if (forwarderBalance == 0) {
return;
}
if (!instance.transfer(parentAddress, forwarderBalance)) {
throw;
}
TokensFlushed(tokenContractAddress, forwarderBalance);
}
/**
* It is possible that funds were sent to this address before the contract was deployed.
* We can flush those funds to the destination address.
*/
function flush() {
if (!parentAddress.call.value(this.balance)())
throw;
}
}