Dappsys

Safe, simple, flexible building-blocks for smart-contract systems.


Auth
ds-auth ds-guard ds-roles
Token
ds-token ds-vault
Oracle
ds-cache ds-value
Util
ds-exec ds-math ds-note ds-proxy ds-stop ds-thing
Gov
ds-multisig

DSProxy

Execute transactions & sequences of transactions by proxy

This contract implements a very useful utility called a proxy. It is deployed as a standalone contract, and can then be used by the owner to execute code.

A user would pass in the bytecode for the contract as well as the calldata for the function they want to execute.

The proxy will create a contract using the bytecode. It will then delegatecall the function and arguments specified in the calldata. Loading in this code is more efficient than jumping to it.

Use Cases

1. Allow actions to be executed through the proxy identity

This can be very useful for securing complex applications. Because delegatecall retains msg.sender and msg.value properties, internal functions can be set to only accept calls coming from the proxy through an ownership model like ds-auth. In this manner as long as the proxy is not compromised, the internal system is protected from outsider access. Should the owner of the internal calls ever need to be changed, this is as simple as updating the owner of ds-proxy rather than manualy updating each individual internal function call, making it much more secure and adaptable.

2. Execute a sequence of actions atomically

Due to restrictions in the EVM instruction set such as being unable to be nested dynamically sized types and arguments, 1 transaction could be done at a time. Since ds-proxy takes in bytecode of a contract, rather than relying on a pre-deployed contract, customized script contracts can be used. These script contracts share a very a important property in that they enable a sequence of actions to be executed atomically (all or nothing). This prevents having to manually rollback writes to contracts when a single transaction fails in a set of transactions.

Example Usage

Note: the examples assume the user is using Dapphub’s dapp and seth

  1. Deploy DSProxyFactory. (Optional - DSProxy can be deployed directly)

    dapp create DSProxyFactory

  2. Call the build function in DSProxyFactory to create a proxy for you. (Optional)

    seth send <DSProxyFactoryAddr> "build()(address)"

  3. Create a contract and compile using solc.

    dapp build MyCustomContract

  4. Get the calldata for the function and arguments you want to execute

    seth calldata "<functionName>(<argType1>,<argType2>...<argTypeN>)(<returnArgType>)" <arg1> <arg2> <argN>

  5. Pass the contract bytecode and calldata to the execute function inside the deployed DSProxy.
    seth send <DSProxyAddr> "execute(bytes,bytes)(bytes32)" <ContractByteCode> <CallData>

  // proxy.sol - execute actions atomically through the proxy's identity

// Copyright (C) 2017  DappHub, LLC

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity ^0.4.13;

import "ds-auth/auth.sol";
import "ds-note/note.sol";

// DSProxy
// Allows code execution using a persistant identity This can be very
// useful to execute a sequence of atomic actions. Since the owner of
// the proxy can be changed, this allows for dynamic ownership models
// i.e. a multisig
contract DSProxy is DSAuth, DSNote {
    DSProxyCache public cache;  // global cache for contracts

    function DSProxy(address _cacheAddr) public {
        require(setCache(_cacheAddr));
    }

    function() public payable {
    }

    // use the proxy to execute calldata _data on contract _code
    function execute(bytes _code, bytes _data)
        public
        payable
        returns (address target, bytes32 response)
    {
        target = cache.read(_code);
        if (target == 0x0) {
            // deploy contract & store its address in cache
            target = cache.write(_code);
        }

        response = execute(target, _data);
    }

    function execute(address _target, bytes _data)
        public
        auth
        note
        payable
        returns (bytes32 response)
    {
        require(_target != 0x0);

        // call contract in current context
        assembly {
            let succeeded := delegatecall(sub(gas, 5000), _target, add(_data, 0x20), mload(_data), 0, 32)
            response := mload(0)      // load delegatecall output
            switch iszero(succeeded)
            case 1 {
                // throw if delegatecall failed
                revert(0, 0)
            }
        }
    }

    //set new cache
    function setCache(address _cacheAddr)
        public
        auth
        note
        returns (bool)
    {
        require(_cacheAddr != 0x0);        // invalid cache address
        cache = DSProxyCache(_cacheAddr);  // overwrite cache
        return true;
    }
}

// DSProxyFactory
// This factory deploys new proxy instances through build()
// Deployed proxy addresses are logged
contract DSProxyFactory {
    event Created(address indexed sender, address proxy, address cache);
    mapping(address=>bool) public isProxy;
    DSProxyCache public cache = new DSProxyCache();

    // deploys a new proxy instance
    // sets owner of proxy to caller
    function build() public returns (DSProxy proxy) {
        proxy = build(msg.sender);
    }

    // deploys a new proxy instance
    // sets custom owner of proxy
    function build(address owner) public returns (DSProxy proxy) {
        proxy = new DSProxy(cache);
        Created(owner, address(proxy), address(cache));
        proxy.setOwner(owner);
        isProxy[proxy] = true;
    }
}

// DSProxyCache
// This global cache stores addresses of contracts previously deployed
// by a proxy. This saves gas from repeat deployment of the same
// contracts and eliminates blockchain bloat.

// By default, all proxies deployed from the same factory store
// contracts in the same cache. The cache a proxy instance uses can be
// changed.  The cache uses the sha3 hash of a contract's bytecode to
// lookup the address
contract DSProxyCache {
    mapping(bytes32 => address) cache;

    function read(bytes _code) public view returns (address) {
        bytes32 hash = keccak256(_code);
        return cache[hash];
    }

    function write(bytes _code) public returns (address target) {
        assembly {
            target := create(0, add(_code, 0x20), mload(_code))
            switch iszero(extcodesize(target))
            case 1 {
                // throw if contract failed to deploy
                revert(0, 0)
            }
        }
        bytes32 hash = keccak256(_code);
        cache[hash] = target;
    }
}

Tools for dapps

We believe that the free software movement is the most important cultural predecessor to the modern-day renaissance in decentralized technologies.

To catalyze the growth of this ecosystem, and to empower hackers to participate, we’re building a comprehensive suite of blockchain-oriented developer tools in the spirit of the Unix philosophy.

Dapp is all you need to start developing for Ethereum. It creates new dapps, runs Solidity unit tests, debugs, deploys, launches testnets, and more.

Seth is a handy tool for slicing and dicing transactions, querying the blockchain, converting between data formats, performing remote calls, and other everyday tasks.

Hevm is our own EVM implementation with a nimble terminal-based Solidity debugger. It’s used for dapp test and dapp debug.

Evmdis is an EVM disassembler written and maintained by Nick Johnson. It’s useful to make sense of EVM bytecode, especially when developing contracts at the assembly or raw bytecode level.

Dappsys - smart contract building blocks

We also maintain Dappsys, an audited collection of smart contract building blocks designed to complement each other. They include;

Using these proven parts lets us focus on the novel features of the systems we develop. We share Dappsys to benefit the smart contract ecosystem.