I wrote this contract below that would create a factory contract which generates user deposit addresses. the user addresses would forward any deposit to the authorized caller on the factory contract, I have also created a function that would sweep ERC20 tokens from the user's deposit wallets.
my issue is that the sweeping function seems to fail each time and I have no idea why!
pragma solidity 0.4.21;
//The Factory Contract
contract Factory {
//Contract Owner
address public owner;
//The authorized caller to the Users accounts
address public authorizedCaller;
//The suggested Owner Candidate
address private ownerCandidate;
//The suggested authorized Caller Candidate
address private authorizedCallerCandidate;
// contains user wallets
mapping (address => bool) public UserWallets;
/*
the owner Candidate hash of the random
generated 256 bit key using keccak256 (web3.sha3)
*/
bytes32 private ownerCandidateKeyHash;
/*
the authorized Caller Candidat hash of the random
generated 256 bit key using keccak256 (web3.sha3)
*/
bytes32 private authorizedCallerCandidateKeyHash;
// a deposit event to a user contract address
event Transaction(address from, address to, uint256 value);
// event about an accepted new owner
event NewOwner(address owner);
// event about a new accepted authorized caller
event NewAuthorizedCaller(address authorizedCaller);
//event about a new generated user address
event NewUserWallet(address newWallet);
function Factory() public {
owner = msg.sender;
authorizedCaller = msg.sender;
}
// the only owner modifier
modifier onlyOwner {
assert(msg.sender == owner);
_;
}
// the only authorized called modifier
modifier onlyAuthorizedCaller {
assert(msg.sender == authorizedCaller);
_;
}
modifier onlyUserWallets {
assert(UserWallets[msg.sender] == true);
_;
}
// the only owner candidtate modifier
modifier onlyOwnerCandidate(bytes32 key) {
assert(msg.sender == ownerCandidate);
assert(keccak256(key) == ownerCandidateKeyHash);
_;
}
// the only authorized caller candidtate modifier
modifier onlyAuthorizedCallerCandidate(bytes32 key) {
assert(msg.sender == authorizedCallerCandidate);
assert(keccak256(key) == authorizedCallerCandidateKeyHash);
_;
}
/*
transfer ownership function, create a transfer proposal that needs
to be accepted by the new owner candidtate:
candidate address is needed
keyHash: generate a 256 bit key and hash it using keccak256 (web3.sha3)
share the key with the candidate through a secure communication channel
*/
function transferOwnership(address candidate, bytes32 keyHash) public onlyOwner {
ownerCandidate = candidate;
ownerCandidateKeyHash = keyHash;
}
/*
the candidate needs to have the key that generates the correct hash
to be able to accept ownership of the contract, the key should be
communicated through secure channels
*/
function acceptOwnership(bytes32 key) external onlyOwnerCandidate(key) {
owner = ownerCandidate;
emit NewOwner(ownerCandidate);
}
/*
change the Authorized caller function, create a transfer proposal that needs
to be accepted by the new Authorized caller candidtate:
candidate address is needed
keyHash: generate a 256 bit key and hash it using keccak256 (web3.sha3)
share the key with the candidate through a secure communication channel
*/
function changeAuthorizedCaller(address candidate, bytes32 keyHash) public onlyOwner {
authorizedCallerCandidate = candidate;
authorizedCallerCandidateKeyHash = keyHash;
}
/*
the candidate needs to have the key that generates the correct hash
to be able to accept ownership of the contract, the key should be
communicated through secure channels
*/
function acceptAuthorization(bytes32 key) external onlyAuthorizedCallerCandidate(key) {
authorizedCaller = authorizedCallerCandidate;
emit NewAuthorizedCaller(authorizedCallerCandidate);
}
// this function generates users wallets
function CreateNewUserWallet() public onlyAuthorizedCaller returns (address newWallet) {
newWallet = new UserWallet(address(this));
UserWallets[newWallet] = true;
emit NewUserWallet(newWallet);
}
// in case we need to make some calls using this contract
function execute(address to, uint256 value, bytes data) public onlyAuthorizedCaller {
require(to.call.value(value)(data));
}
// the function logs value transactions to a user wallet
function LogTransaction(address from, address to, uint256 value) external onlyUserWallets {
emit Transaction(from, to, value);
}
}
// User wallet
contract UserWallet {
Factory FactoryContract;
/*
a Modifier to make sure that ony the Authorized caller
can call some functions
*/
function UserWallet(address _FactoryContract) public {
FactoryContract = Factory(_FactoryContract);
}
modifier onlyAuthorizedCaller {
assert(msg.sender == FactoryContract.authorizedCaller());
_;
}
/*
Allow payments and sweep them automatically to the Authorized caller's address
*/
function() public payable {
if (msg.value > 0) {
FactoryContract.authorizedCaller().transfer(msg.value);
FactoryContract.LogTransaction(msg.sender, this, msg.value);
}
}
/*
Sweep ERC20 tokens sent to this wallet by simple providing
the token Contract Address (ERC20 contract address)
*/
function SweepTokens(address tokenContractAddress) public onlyAuthorizedCaller {
ERC20Interface Token = ERC20Interface(tokenContractAddress);
address Wallet = address(this);
address AuthorizedCaller = FactoryContract.authorizedCaller();
uint256 TokenBalance = Token.balanceOf(Wallet);
if (TokenBalance > 0) {
require(Token.transfer(AuthorizedCaller, TokenBalance));
}
}
/*
execute function allow us to still execute any call on
this contract making it almost like a normal account address
*/
function execute(address to, uint256 value, bytes data) public onlyAuthorizedCaller {
require(to.call.value(value)(data));
}
}
// ERC20 Interface
contract ERC20Interface {
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}