pragma solidity ^0.4.13;
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
contract TokenRecipient {
event ReceivedEther(address indexed sender, uint amount);
event ReceivedTokens(address indexed from, uint256 value, address indexed token, bytes extraData);
/**
* @dev Receive tokens and generate a log event
* @param from Address from which to transfer tokens
* @param value Amount of tokens to transfer
* @param token Address of token
* @param extraData Additional data to log
*/
function receiveApproval(address from, uint256 value, address token, bytes extraData) public {
ERC20 t = ERC20(token);
require(t.transferFrom(from, this, value));
ReceivedTokens(from, value, token, extraData);
}
/**
* @dev Receive Ether and generate a log event
*/
function () payable public {
ReceivedEther(msg.sender, msg.value);
}
}
contract AuthenticatedProxy is TokenRecipient {
/* Address which owns this proxy. */
address public user;
/* Associated registry with contract authentication information. */
ProxyRegistry public registry;
/* Whether access has been revoked. */
bool public revoked;
/* Delegate call could be used to atomically transfer multiple assets owned by the proxy contract with one order. */
enum HowToCall { Call, DelegateCall }
/* Event fired when the proxy access is revoked or unrevoked. */
event Revoked(bool revoked);
/**
* Create an AuthenticatedProxy
*
* @param addrUser Address of user on whose behalf this proxy will act
* @param addrRegistry Address of ProxyRegistry contract which will manage this proxy
*/
function AuthenticatedProxy(address addrUser, ProxyRegistry addrRegistry) public {
user = addrUser;
registry = addrRegistry;
}
/**
* Set the revoked flag (allows a user to revoke ProxyRegistry access)
*
* @dev Can be called by the user only
* @param revoke Whether or not to revoke access
*/
function setRevoke(bool revoke)
public
{
require(msg.sender == user);
revoked = revoke;
Revoked(revoke);
}
/**
* Execute a message call from the proxy contract
*
* @dev Can be called by the user, or by a contract authorized by the registry as long as the user has not revoked access
* @param dest Address to which the call will be sent
* @param howToCall Which kind of call to make
* @param calldata Calldata to send
* @return Result of the call (success or failure)
*/
function proxy(address dest, HowToCall howToCall, bytes calldata)
public
returns (bool result)
{
require(msg.sender == user || (!revoked && registry.contracts(msg.sender)));
if (howToCall == HowToCall.Call) {
result = dest.call(calldata);
} else if (howToCall == HowToCall.DelegateCall) {
result = dest.delegatecall(calldata);
}
return result;
}
/**
* Execute a message call and assert success
*
* @dev Same functionality as `proxy`, just asserts the return value
* @param dest Address to which the call will be sent
* @param howToCall What kind of call to make
* @param calldata Calldata to send
*/
function proxyAssert(address dest, HowToCall howToCall, bytes calldata)
public
{
require(proxy(dest, howToCall, calldata));
}
}
contract ProxyRegistry is Ownable {
/* Authenticated proxies by user. */
mapping(address => AuthenticatedProxy) public proxies;
/* Contracts pending access. */
mapping(address => uint) public pending;
/* Contracts allowed to call those proxies. */
mapping(address => bool) public contracts;
/* Delay period for adding an authenticated contract.
This mitigates a particular class of potential attack on the Wyvern DAO (which owns this registry) - if at any point the value of assets held by proxy contracts exceeded the value of half the WYV supply (votes in the DAO),
a malicious but rational attacker could buy half the Wyvern and grant themselves access to all the proxy contracts. A delay period renders this attack nonthreatening - given two weeks, if that happened, users would have
plenty of time to notice and transfer their assets.
*/
uint public DELAY_PERIOD = 2 weeks;
/**
* Start the process to enable access for specified contract. Subject to delay period.
*
* @dev ProxyRegistry owner only
* @param addr Address to which to grant permissions
*/
function startGrantAuthentication (address addr)
public
onlyOwner
{
require(!contracts[addr] && pending[addr] == 0);
pending[addr] = now;
}
/**
* End the process to nable access for specified contract after delay period has passed.
*
* @dev ProxyRegistry owner only
* @param addr Address to which to grant permissions
*/
function endGrantAuthentication (address addr)
public
onlyOwner
{
require(!contracts[addr] && pending[addr] != 0 && ((pending[addr] + DELAY_PERIOD) < now));
pending[addr] = 0;
contracts[addr] = true;
}
/**
* Revoke access for specified contract. Can be done instantly.
*
* @dev ProxyRegistry owner only
* @param addr Address of which to revoke permissions
*/
function revokeAuthentication (address addr)
public
onlyOwner
{
contracts[addr] = false;
}
/**
* Register a proxy contract with this registry
*
* @dev Must be called by the user which the proxy is for, creates a new AuthenticatedProxy
* @return New AuthenticatedProxy contract
*/
function registerProxy()
public
returns (AuthenticatedProxy proxy)
{
require(proxies[msg.sender] == address(0));
proxy = new AuthenticatedProxy(msg.sender, this);
proxies[msg.sender] = proxy;
return proxy;
}
}
contract WyvernProxyRegistry is ProxyRegistry {
string public constant name = "Project Wyvern Proxy Registry";
/* Whether the initial auth address has been set. */
bool public initialAddressSet = false;
/**
* Grant authentication to the initial Exchange protocol contract
*
* @dev No delay, can only be called once - after that the standard registry process with a delay must be used
* @param authAddress Address of the contract to grant authentication
*/
function grantInitialAuthentication (address authAddress)
onlyOwner
public
{
require(!initialAddressSet);
initialAddressSet = true;
contracts[authAddress] = true;
}
}
{
"compilationTarget": {
"AuthenticatedProxy.sol": "AuthenticatedProxy"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"dest","type":"address"},{"name":"howToCall","type":"uint8"},{"name":"calldata","type":"bytes"}],"name":"proxy","outputs":[{"name":"result","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"dest","type":"address"},{"name":"howToCall","type":"uint8"},{"name":"calldata","type":"bytes"}],"name":"proxyAssert","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"revoke","type":"bool"}],"name":"setRevoke","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"user","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"revoked","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"value","type":"uint256"},{"name":"token","type":"address"},{"name":"extraData","type":"bytes"}],"name":"receiveApproval","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"addrUser","type":"address"},{"name":"addrRegistry","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"revoked","type":"bool"}],"name":"Revoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"ReceivedEther","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":true,"name":"token","type":"address"},{"indexed":false,"name":"extraData","type":"bytes"}],"name":"ReceivedTokens","type":"event"}]