Bytecode verified via sibling
This contract shares identical runtime bytecode with a verified contract (0x0f37ccab...) which has been verified through compiler archaeology.
Bancor-lineage per-user withdrawal wallet, first of 7,847 identical siblings from Sep 2018. Source verified by EthereumHistory via linked-library bytecode match.
Key Facts
Description
This contract is the first of 7,847 identical SmartWallet deployments from Bancor's per-user withdrawal system, live from September 27, 2018. Each SmartWallet holds a single storage-slot Wallet struct and forwards owner-facing calls (setUserWithdrawalAccount, transferToUserWithdrawalAccount, requestWithdraw, performUserWithdraw) to entry points on a shared SmartWalletLib deployed once and linked into every instance.
The source is the same SmartWallet already Etherscan-verified at 0x2df514a060bbd105ea428182e8b140454f426d15 (compiler v0.4.24+commit.e67f0147, optimizer ON runs=200, matching swarm metadata hash 0x5073597116b7...). The only difference between the reference deployment's on-chain runtime and this target's runtime is the linked SmartWalletLib address, which is embedded at exactly four positions in the 971-byte runtime.
The target's SmartWalletLib is at 0x74ba8c8f53809bd3baa0c243350cb989f6c9dc3c. Substituting that library address into the reference-verified runtime produces byte-for-byte match against on-chain eth_getCode.
Proof: cartoonitunes/awesome-ethereum-proofs, folder smartwallet-linked-0xfc247caf.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Byzantium Era
First Metropolis hard fork. Added zk-SNARK precompiles, REVERT opcode, and staticcall.
Bytecode Overview
Verified Source Available
This contract has verified source code on Etherscan.
Show source code (Solidity)
pragma solidity ^0.4.24;
contract IERC20Token {
// these functions aren't abstract since the compiler emits automatically generated getter functions as external
function name() public constant returns (string) {}
function symbol() public constant returns (string) {}
function decimals() public constant returns (uint8) {}
function totalSupply() public constant returns (uint256) {}
function balanceOf(address _owner) public constant returns (uint256) { _owner; }
function allowance(address _owner, address _spender) public constant returns (uint256) { _owner; _spender; }
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
}
contract Ownable {
address public owner;
address public newOwner;
event OwnerUpdate(address _prevOwner, address _newOwner);
/*
@dev constructor
*/
constructor (address _owner) public {
owner = _owner;
}
/*
@dev allows execution by the owner only
*/
modifier ownerOnly {
require(msg.sender == owner);
_;
}
/*
@dev allows transferring the contract ownership
the new owner still needs to accept the transfer
can only be called by the contract owner
@param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public ownerOnly {
require(_newOwner != owner);
newOwner = _newOwner;
}
/*
@dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
contract Utils {
/*
@dev constructor
*/
constructor() public {
}
/*
@dev verifies that an amount is greater than zero
*/
modifier greaterThanZero(uint256 _amount) {
require(_amount > 0);
_;
}
/*
@dev validates an address - currently only checks that it isn't null
*/
modifier validAddress(address _address) {
require(_address != 0x0);
_;
}
/*
@dev verifies that the address is different than this contract address
*/
modifier notThis(address _address) {
require(_address != address(this));
_;
}
/*
@dev verifies that the string is not empty
*/
modifier notEmpty(string _str) {
require(bytes(_str).length > 0);
_;
}
// Overflow protected math functions
/*
@dev returns the sum of _x and _y, asserts if the calculation overflows
@param _x value 1
@param _y value 2
@return sum
*/
function safeAdd(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
assert(z >= _x);
return z;
}
/*
@dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
@param _x minuend
@param _y subtrahend
@return difference
*/
function safeSub(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_x >= _y);
return _x - _y;
}
/*
@dev returns the product of multiplying _x by _y, asserts if the calculation overflows
@param _x factor 1
@param _y factor 2
@return product
*/
function safeMul(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x * _y;
assert(_x == 0 || z / _x == _y);
return z;
}
}
contract WithdrawalConfigurations is Ownable, Utils {
/*
* Members
*/
uint public minWithdrawalCoolingPeriod;
uint constant maxWithdrawalCoolingPeriod = 12 * 1 weeks; // = 14515200 seconds
uint public withdrawalCoolingPeriod;
/*
* Events
*/
event WithdrawalRequested(address _sender, address _smartWallet);
event SetWithdrawalCoolingPeriod(uint _withdrawalCoolingPeriod);
/*
@dev constructor
@param _withdrawalCoolingPeriod The cooling period
@param _minWithdrawalCoolingPeriod The minimum time from withdraw request to allow performing it
*/
constructor (uint _withdrawalCoolingPeriod, uint _minWithdrawalCoolingPeriod)
Ownable(msg.sender)
public
{
require(_withdrawalCoolingPeriod <= maxWithdrawalCoolingPeriod &&
_withdrawalCoolingPeriod >= _minWithdrawalCoolingPeriod);
require(_minWithdrawalCoolingPeriod >= 0);
minWithdrawalCoolingPeriod = _minWithdrawalCoolingPeriod;
withdrawalCoolingPeriod = _withdrawalCoolingPeriod;
}
/*
@dev Get the withdrawalCoolingPeriod parameter value.
*/
function getWithdrawalCoolingPeriod() external view returns(uint) {
return withdrawalCoolingPeriod;
}
/*
@dev Set the withdrawalCoolingPeriod parameter value.
@param _withdrawalCoolingPeriod Cooling period in seconds
*/
function setWithdrawalCoolingPeriod(uint _withdrawalCoolingPeriod)
ownerOnly()
public
{
require (_withdrawalCoolingPeriod <= maxWithdrawalCoolingPeriod &&
_withdrawalCoolingPeriod >= minWithdrawalCoolingPeriod);
withdrawalCoolingPeriod = _withdrawalCoolingPeriod;
emit SetWithdrawalCoolingPeriod(_withdrawalCoolingPeriod);
}
/*
@dev Fire the WithdrawalRequested event.
@param _sender The user account, activating this request
@param _smartWallet The smart wallet that the request was called upon
*/
function emitWithrawalRequestEvent(address _sender, address _smartWallet)
public
{
emit WithdrawalRequested(_sender, _smartWallet);
}
}
library SmartWalletLib {
/*
* Structs
*/
struct Wallet {
address operatorAccount;
address userWithdrawalAccount;
address feesAccount;
uint withdrawAllowedAt; // In seconds
}
/*
* Members
*/
string constant VERSION = "1.1";
address constant withdrawalConfigurationsContract = 0xDdD336eAad17F1D40cc81997Fb956608f00639FF;
/*
* Modifiers
*/
modifier validAddress(address _address) {
require(_address != 0x0);
_;
}
modifier addressNotSet(address _address) {
require(_address == 0);
_;
}
modifier operatorOnly(address _operatorAccount) {
require(msg.sender == _operatorAccount);
_;
}
modifier userWithdrawalAccountOnly(Wallet storage _self) {
require(msg.sender == _self.userWithdrawalAccount);
_;
}
/*
* Events
*/
event TransferToUserWithdrawalAccount(address _token, address _userWithdrawalAccount, uint _amount, address _feesToken, address _feesAccount, uint _fee);
event SetUserWithdrawalAccount(address _userWithdrawalAccount);
event PerformUserWithdraw(address _token, address _userWithdrawalAccount, uint _amount);
/*
@dev Initialize the wallet with the operator and backupAccount address
@param _self Wallet storage
@param _operator The operator account
@param _feesAccount The account to transfer fees to
*/
function initWallet(Wallet storage _self, address _operator, address _feesAccount)
public
validAddress(_operator)
validAddress(_feesAccount)
{
_self.operatorAccount = _operator;
_self.feesAccount = _feesAccount;
}
/*
@dev Setting the account of the user to send funds to.
@param _self Wallet storage
@param _userWithdrawalAccount The user account to withdraw funds to
*/
function setUserWithdrawalAccount(Wallet storage _self, address _userWithdrawalAccount)
public
operatorOnly(_self.operatorAccount)
validAddress(_userWithdrawalAccount)
addressNotSet(_self.userWithdrawalAccount)
{
_self.userWithdrawalAccount = _userWithdrawalAccount;
emit SetUserWithdrawalAccount(_userWithdrawalAccount);
}
/*
@dev Withdraw funds to the user account.
@param _self Wallet storage
@param _token The ERC20 token the owner withdraws from
@param _amount Amount to transfer
@param _fee Fee to transfer
*/
function transferToUserWithdrawalAccount(Wallet storage _self, IERC20Token _token, uint _amount, IERC20Token _feesToken, uint _fee)
public
operatorOnly(_self.operatorAccount)
validAddress(_self.userWithdrawalAccount)
{
if (_fee > 0) {
_feesToken.transfer(_self.feesAccount, _fee);
}
_token.transfer(_self.userWithdrawalAccount, _amount);
emit TransferToUserWithdrawalAccount(_token, _self.userWithdrawalAccount, _amount, _feesToken, _self.feesAccount, _fee);
}
/*
@dev returns the sum of _x and _y, asserts if the calculation overflows
@param _x value 1
@param _y value 2
@return sum
*/
function safeAdd(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
assert(z >= _x);
return z;
}
/*
@dev user request withdraw.
@param _self Wallet storage
@param _token The ERC20 token the owner withdraws from
*/
function requestWithdraw(Wallet storage _self)
public
userWithdrawalAccountOnly(_self)
{
WithdrawalConfigurations withdrawalConfigurations = WithdrawalConfigurations(withdrawalConfigurationsContract);
_self.withdrawAllowedAt = safeAdd(now, withdrawalConfigurations.getWithdrawalCoolingPeriod());
withdrawalConfigurations.emitWithrawalRequestEvent(msg.sender, address(this));
}
/*
@dev user perform withdraw.
@param _self Wallet storage
@param _token The ERC20 token the owner withdraws from
*/
function performUserWithdraw(Wallet storage _self, IERC20Token _token)
public
userWithdrawalAccountOnly(_self)
{
require(_self.withdrawAllowedAt != 0 &&
_self.withdrawAllowedAt <= now );
uint userBalance = _token.balanceOf(this);
_token.transfer(_self.userWithdrawalAccount, userBalance);
emit PerformUserWithdraw(_token, _self.userWithdrawalAccount, userBalance);
}
}
contract SmartWallet {
/*
* Members
*/
using SmartWalletLib for SmartWalletLib.Wallet;
SmartWalletLib.Wallet public wallet;
// Wallet public wallet;
/*
* Events
*/
event TransferToUserWithdrawalAccount(address _token, address _userWithdrawalAccount, uint _amount, address _feesToken, address _feesAccount, uint _fee);
event SetUserWithdrawalAccount(address _userWithdrawalAccount);
event PerformUserWithdraw(address _token, address _userWithdrawalAccount, uint _amount);
/*
@dev constructor
@param _backupAccount A default operator's account to send funds to, in cases where the user account is
unavailable or lost
@param _operator The contract operator address
@param _feesAccount The account to transfer fees to
*/
constructor (address _operator, address _feesAccount) public {
wallet.initWallet(_operator, _feesAccount);
}
/*
@dev Setting the account of the user to send funds to.
@param _userWithdrawalAccount The user account to withdraw funds to
*/
function setUserWithdrawalAccount(address _userWithdrawalAccount) public {
wallet.setUserWithdrawalAccount(_userWithdrawalAccount);
}
/*
@dev Withdraw funds to the user account.
@param _token The ERC20 token the owner withdraws from
@param _amount Amount to transfer
*/
function transferToUserWithdrawalAccount(IERC20Token _token, uint _amount, IERC20Token _feesToken, uint _fee) public {
wallet.transferToUserWithdrawalAccount(_token, _amount, _feesToken, _fee);
}
/*
@dev Allows the user to request a withdraw of his/her placements
@param _token The ERC20 token the user wishes to withdraw from
*/
function requestWithdraw() public {
wallet.requestWithdraw();
}
/*
@dev Allows the user to perform the requestWithdraw operation
@param _token The ERC20 token the user withdraws from
*/
function performUserWithdraw(IERC20Token _token) public {
wallet.performUserWithdraw(_token);
}
}External Links
Related contracts
Msg
Same eraMinimal contract that stores a single public string in state, exposed via the auto-generated m() getter. First of 281 identical siblings. Source verified by EthereumHistory.
0x2c8f58...37b173January 13, 2018Wallet
Same eraToken collection wallet that moves an entire token balance to a chosen address, without the ERC223 callback handler.
0x002204...531195January 19, 2018Wallet
Same eraOwner wallet that forwards ether to its owner, logs any call from a stranger with the full calldata, and lets the owner execute arbitrary calls.
0x001feb...9e7a4fJanuary 20, 2018Forwarder
Same eraNinety-three bytes with one hardcoded destination: every payment received is transferred straight back out to the same address.
0x458353...a562deJanuary 20, 2018Sweeper
Same eraMinimal ETH sweeper, first of 1,900 identical deployments in a 2-day burst in January 2018. All sweep to a single hardcoded destination. Source verified by EthereumHistory.
0xeb15f6...6cb3e1January 23, 2018Sweep
Same eraETH sweep-to-fixed-recipient — fallback forwards the contract's entire balance to `0xd293a88c…8d4` via `.send()` and reverts on failure. Massive cluster of 1899 identical deployments — likely deposit-address contracts for a single custodian.
0x00188b...b260a7January 23, 2018