GithubHelp home page GithubHelp logo

tokencard / contracts Goto Github PK

View Code? Open in Web Editor NEW
95.0 95.0 40.0 164.51 MB

The Consumer Contract Wallet

License: GNU General Public License v3.0

Shell 1.19% Go 65.44% Solidity 33.37%
ethereum golang smart-contracts solidity

contracts's Issues

In AddToken

Loop through and check if token exists by looping through the array first, to save some gas ...

Refactor whitelist operation checker into a new modifier

Both submitWhitelistAddition and submitWhitelistRemoval have the following check:

require(!submittedWhitelistAddition && !submittedWhitelistRemoval, "whitelist operation has already been submitted");

This should be put into a new modifier, e.g. noWhitelistOperationInProgress for better maintainability and code re-use.

Store only a single list of supported tokens

At the moment we store a map of supported tokens and also an array of supported tokens:

 mapping (address => Erc20Token) public tokens;
 address[] private _contractAddresses;

This is quite redundant and we can most likely find a better solution.

External vs Public

In the Wallet we use external a lot more than public, my understand is that there are gas effeciencies with External, need help here ...

spendAvailable() returns spendLimit

function spendAvailable() external view returns (uint) {
        if (now > _spendLimitDay + 24 hours) {
            return spendLimit;
        } else {
            return _spendAvailable;
        }
    }

spendLimit might not actually reflect _spendAvailable.
_spendAvailable should always be returned.

Subsequently (and perhaps unclear to me),

    /// @dev Confirm pending set daily limit operation.
    function confirmSpendLimit() external onlyController {
        // Require that the operation has been submitted.
        require(submittedSpendLimit, "spend limit has not been submitted");
        // Modify spend limit based on the pending value.
        modifySpendLimit(pendingSpendLimit);
        // Emit the set limit event.
        emit SetSpendLimit(msg.sender, pendingSpendLimit);
        // Reset the submission flag.
        submittedSpendLimit = false;
        // Reset pending daily limit.
        pendingSpendLimit = 0;
    }

    /// @dev Update available spend limit based on the daily reset.
    function updateSpendAvailable() internal {
        if (now > _spendLimitDay + 24 hours) {
            // Advance the current day by how many days have passed.
            uint extraDays = (now - _spendLimitDay) / 24 hours;
            _spendLimitDay += extraDays * 24 hours;
            // Set the available limit to the current spend limit.
            _spendAvailable = spendLimit;
        }
    }

    /// @dev Modify the spend limit and spend available based on the provided value.
    /// @dev _amount is the daily limit amount in wei.
    function modifySpendLimit(uint _amount) private {
        // Account for the spend limit daily reset.
        updateSpendAvailable();
        // Set the daily limit to the provided amount.
        spendLimit = _amount;
        // Lower the available limit if it's higher than the new daily limit.
        if (_spendAvailable > spendLimit) {
            _spendAvailable = spendLimit;
        }
    }

There isn't a 24 hour wait (or remainder of hours to wait before resetting the limit) to stop confirmSpendLimit() from updating the limit and it does not set _spendLimitDay to now

Underscore naming convention

In the wallet contract I used the convention of using a leading underscore _foo for:

  1. Private variables
  2. Private methods
  3. Function parameters

It would be good to use the same approach in the oracle, for example instead of:

function addTokenBatch (address[] tokenIDs, string labels, uint8[] decimals) public onlyController

use:

function addTokenBatch (address[] _tokenIDs, string _labels, uint8[] _decimals) public onlyController 

ownable.sol

in acceptOwnership() there's require(_newOwner != address(0), "owner cannot be set to 0x0000000000000000000000000000000000000000");. Shouldn't this line also exist in transferOwnership() for address _account?

Freeze wallet functionality

Would it be a good idea to add a freeze() function that can be called only by the owner which stops all wallet functionality, and a corresponding unfreeze() function which can be called only by the controller and in turn resumes the wallet functionality?

This would be akin to the freeze operation that popular challenger banks offer. Where the user can just have a panic button and then they have to contact us to unfreeze the wallet. Or some other variation of this.

@mischat

order of confirming adding/removal to/from the whitelist

We have no cross-checks of which addresses are in the _submittedRemoval and _submittedAddition. So if the same address is in the both list, depending on the order in which the controller accepts, the address might end up being whitelisted or not.
Probably we should not allow adding of addresses while removal is in process and vice versa.

balance() (kinda) defaults to ERC20

/// @dev Returns the amount of an asset owned by the contract.
    /// @param _asset address of an ERC20 token or 0x0 for ether.
    /// @return balance associated with the wallet address in wei.
    function balance(address _asset) external view returns (uint) {
        if (_asset != 0x0) {
            return ERC20(_asset).balanceOf(this);
        } else {
            return address(this).balance;
        }
    }

what happens if this is called with an address asset that's not ETH or ERC20? What would return ERC20(_asset).balanceOf(this); do? I'd assume it would just fall over?

is there a nicer way to detect if an asset is an ERC20 token? if possible, it would be nice if have this in a if, else if, else fail kind of statement

_topupLimitDay in Wallet contract should be initialized to now

The _topupLimitDay member field should get set to now in the constructor - at the moment it is 0, which means the top up day starts at midnight, unlike the spend limit which will start at the time the contract is deployed. These two "limit days" should be in sync.

We should probably set the address resolver explicitly

Currently when the constructor receives a 0x0 address in the constructor it sets the address of the resolver to a hardcoded value:

        if (_oraclizeAddrResolver == 0x0) {
          _oraclizeAddrResolver = 0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475;
        }

I think that this is perhaps unnecessary and we can remove it.

The Controllable interface should be split out into a separate file, thus allowing the concrete Controllable implementation to inherit from it

Why doesn’t the Controllable contract extend the controller interface? Since it’s designed to implement it.

Also, interface could be renamed to IController or something similar to make it clear what it is and to make the above implementation possible without a name clash.

We could repeat this process for other parts of the code where we are using an interface, e.g. Resolver

oracle.sol base64decode()

should probably do a length check of _encoded before

if (keccak256(_encoded[length - 2]) == keccak256("=")) {
            length -= 2;
        } else if (keccak256(_encoded[length - 1]) == keccak256("=")) {
            length -= 1;
        }

Two very similar modifiers

There are two modifiers that check for the same / opposite value:

    modifier tokenSupported(address tokenID) {
        require(tokens[tokenID].supported);
        _;
    }

    modifier tokenNotSupported(address tokenID) {
        require(!tokens[tokenID].supported);
        _;
    }

We can probably merge it into a single modifier.

A Question around Deleting from the list of supportedTokens

This looks strange to me ...

It looks like we are deleting the property supported in the ERC20Token Struct ... surely this doesn't make any sense :

/**
    * @dev remove a token from the list of supported ones
    * @param tokenID token contract addresses
    */
    function removeToken(address tokenID) public onlyController tokenSupported(tokenID) {
        delete tokens[tokenID].supported;

        // Check if the address matches up to one token before the last one
        // the tokenSupported() modifier ensures that the token address actually exists.
        // If no match is found in the loop, it means that the last address was the desired one, simply reduce the size by one in any case.
        uint contractAddressesLength = _contractAddresses.length - 1;
        for (uint i=0; i<contractAddressesLength; i++)
            if (_contractAddresses[i] == tokenID) {
                _contractAddresses[i] = _contractAddresses[contractAddressesLength];
                break;
            }
        _contractAddresses.length--;

        emit TokenRemoval(tokenID);
    }```

Improve comments and remove commented out code

Some comments are inconsistent or missing, should standardize the comment style and use it everywhere.

Also we should remove the commented out code from the contract:

    function __callback(bytes32 queryId, string result, bytes proof) public onlyOraclize {

        require(tokens[validIDs[queryId]].supported);//must be a valid token.

        /* if ((proof.length > 0) && (nativeProof_verify(result, proof, cc_pubkey))) {
          TKNETH = parseInt(result, 6);
          delete validIDs[queryId];
          last_update_timestamp = now;
        } */

        uint rate = parseInt(result, 18);

        tokens[validIDs[queryId]].rate = rate; //transform rate(string) to uint (wei precision)
        emit RateUpdated(validIDs[queryId], rate);
        delete validIDs[queryId]; //remove mapping
    }

Emit token addition events in the batch method

Currently we don't emit any events in the addTokenBatch method, we should re-use the same event that the addToken method uses:

    function addTokenBatch (address[] tokenIDs, string labels, uint8[] decimals) public onlyController {
        require(tokenIDs.length == decimals.length);

        // Convert strings into the library's 'slice' format.
        strings.slice memory labelSlice = labels.toSlice();
        strings.slice memory delim = ".".toSlice();

        uint numTokenLabels = labelSlice.count(delim) + 1; //the number of labels is +1 of thenumber of '.' ["t1.t2.t3"] string expected
        require(numTokenLabels == decimals.length);

        for (uint i = 0; i < numTokenLabels; i++) {
            if(!tokens[tokenIDs[i]].supported){
                _contractAddresses.push(tokenIDs[i]); //push token to the array
                tokens[tokenIDs[i]].label = labelSlice.split(delim).toString();//split the string with a '.' delimiter
                tokens[tokenIDs[i]].decimals = decimals[i];
                tokens[tokenIDs[i]].rate = 0; //to be updated later
                tokens[tokenIDs[i]].supported = true;
            }
        }
    }

submitWhitelistAddition() should reject if initializedWhitelist is false

At the moment submitWhitelistAddition has the following the code inside it:

   ....
        // Flag operation as initialized if not initialized already.
        if (!initializedWhitelist) {
            initializedWhitelist = true;
        }
        // Emit the submission event.
        emit SubmitWhitelistAddition(_addresses);

This is bad since, if the submission doesn't get confirmed then we've incorrectly flipped this flag to true.

Instead, submitWhitelistAddition and submitWhitelistRemoval should both check to see if initializedWhitelist is true. If not then these methods should throw.

This ensures that we will always have to call initializeWhitelist first before doing any further operations to the whitelist.

setSpendLimit - emitting an event

currently, setSpendLimit() does not emit any events. It would be useful for the controller to be able to pick up those events in order to know what needs to be confirmed/rejected

Whitelist 0x0

Make sure that people don’t add 0x0 to whitelist ever ...

initializeWhitelist can be called without any addresses

You can call initializeWhitelist without any addresses. This prevents further initialization of the whitelist.

Conceptually I think this could be considered a bug. It also makes it hard to know whether a whitelist has been initialized: BIS will always return an empty array.

I propose we fail the transaction when this function is called without addresses. Then we'll treat an empty array returned from BIS as if the whitelist was not initialized.

transfer non-ERC20

I'm not sure if there's anything that can be done about this but attempting to transfer a non-ERC20 token to a non-whitelisted address would burn the users spend available but also create a Token{} in the Oracle contract.

The attacker/user is probably an idiot for doing this... but i hope this would also open up the possibilities of bugs using non-ERC20 tokens.

Controllable naming (via Ram)

Why doesn’t the Controllable contract extend the controller interface? Since it’s designed to implement it.

Also, interface could be renamed to IController or something similar to make it clear what it is and to make the above implementation possible without a name clash.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.