1

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);
}

Factory example on ropsten

User wallet example on Ropsten

Failing sweep transaction

1

I found out what is the issue, the ERC20 token I was testing with has only doesn't seem to allow sweeping the exact balance, if I subtract a decimal point value the transfer would work just fine.

uint256 TokenBalance = Token.balanceOf(Wallet) - 1;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.